﻿// clang-format off
#include <vf_pch.h>
#include <VFAdapterAPIs.h>
#include <vf_apiutils.h>
#include <vf_tcpclient.h>
#include <vf_tcpserver.h>
#define _USE_MATH_DEFINES
#include <vf_StorageUtils.h>
// clang-format on
#include <comdef.h>
#include <vf_ShowSection3D.h>
#include <vf_laminate.h>
#include <ranges>
#include <vf_oldapi.h>
#include <mbdTreeNodeUtil .h>

constexpr int WM_CHECK = WM_USER + 1;
constexpr int WM_REFRESH_TREE = WM_USER + 2;

using DVec3d = Eigen::Vector<double, 3>;
using DPoint3d = Eigen::Vector<double, 3>;
using DPoint2d = Eigen::Vector<double, 2>;
using DTransform3d = Eigen::Affine3d;
namespace VFAdapter {
extern vfHRESULT QueryLaminateRelateData(LaminateRelateData &data,
                                         std::string const &strLaminateName,
                                         const char *strDBPath);
std::string UnicodeToUtf8(const wchar_t *unicode);

//!
//! @brief 计算箭头线
//! 返回6个点，前两个点为箭杆起终,后面两个点分别为箭簇尾端点
//! @param ptArrowSt 箭头起点
//! @param arrowLen  箭杆长度
//! @param arrowDir  箭头方向(要求单位化)
//! @param arrowPlaneNormal  箭头所在平面法向(要求单位化)
//! @return std::array<DPoint3d, 6>
//!
std::array<DPoint3d, 4> CalcArrow(DPoint3d const &ptArrowSt, double arrowLen,
                                  DVec3d const &arrowDir,
                                  DVec3d const &arrowPlaneNormal) {
  std::array<DPoint3d, 4> rtArr;
  rtArr[0] = ptArrowSt;
  rtArr[1] = ptArrowSt + arrowLen * arrowDir;
  Eigen::AngleAxisd rot30(M_PI / 6, arrowPlaneNormal);
  DVec3d dirArrow30 = -1 * (rot30.toRotationMatrix() * arrowDir);
  DVec3d dirArrowNeg30 = -1 * (rot30.toRotationMatrix().inverse() * arrowDir);
  double arrowSideLen = 0.12 * arrowLen;
  rtArr[2] = rtArr[1] + arrowSideLen * dirArrow30;
  rtArr[3] = rtArr[1] + arrowSideLen * dirArrowNeg30;
  return rtArr;
}
} // namespace VFAdapter

namespace LogUtil {

template <typename T> std::string LogFormat(T &t) {
  std::stringstream ss;
  ss << t;
  return ss.str();
}

template <> std::string LogFormat(std::vector<DPoint3d> &t) {
  std::stringstream ss;
  ss << "[\n";
  ss.precision(std::numeric_limits<double>::digits10);
  for (int i = 0; i < t.size(); i++) {
    ss << i << ":" << t[i].x() << "," << t[i].y() << "," << t[i].z() << "\n";
  }
  ss << "]\n";

  return ss.str();
}

template <> std::string LogFormat(wchar_t *&t) {
  return VFAdapter::UnicodeToUtf8(t);
}

template <> std::string LogFormat(double &t) {
  std::stringstream ss;
  ss.precision(std::numeric_limits<double>::digits10);
  ss << t;
  return ss.str();
}

template <> std::string LogFormat(const wchar_t *&t) {
  return VFAdapter::UnicodeToUtf8(t);
}

template <> std::string LogFormat(const double *&t) {
  std::stringstream ss;
  ss.precision(std::numeric_limits<double>::digits10);
  ss << "[\n";
  int i = 0;
  while (!isnan(*t)) {
    ss << i++ << ":" << *t++ << ",\n";
  }
  ss << "]\n";
  return ss.str();
}

template <> std::string LogFormat(double *&t) {
  std::stringstream ss;
  ss.precision(std::numeric_limits<double>::digits10);
  ss << "[\n";
  int i = 0;
  while (!isnan(*t)) {
    ss << i++ << ":" << *t++ << ",\n";
  }
  ss << "]\n";
  return ss.str();
}

template <> std::string LogFormat(const char *&t) {
  if (nullptr == t) {
    return "nullptr";
  }

  return t;
}
template <> std::string LogFormat(bool &t) { return t ? "true" : "false"; }

template <> std::string LogFormat(const char **&args) {
  // 字符数组前后分别加上[]
  std::string rtStr = "";
  rtStr += "[";
  while (*args) {
    rtStr += *args;
    args++;
    if (*args) {
      rtStr += ",";
    }
  }

  rtStr += "]";
  return rtStr;
}

template <typename U, typename... T>
std::string GetRemainingLogContent(U &head, T &...args) {
  std::string rtStr = "";
  if constexpr (sizeof...(args) == 0) {
    // 最后一个参数尾加)
    std::string tmpStr = LogFormat(head);
    tmpStr += ")";
    return rtStr + tmpStr;
  } else {
    // 参数之间加,
    std::string tmpStr = LogFormat(head);
    if (!tmpStr.ends_with(':')) {
      tmpStr += ",";
    }
    tmpStr += GetRemainingLogContent(args...);
    rtStr += tmpStr;
  }
  return rtStr;
}

//! @brief 获得日志内容
template <typename U, typename... T>
std::string GetLogContent(U head, T... args) {
  // 第一个参数为函数名，后加(
  std::string rtStr = "";
  rtStr += LogFormat(head);
  rtStr += "(";

  // 获得剩余的日志内容
  rtStr += GetRemainingLogContent(args...);
  return rtStr;
}
} // namespace LogUtil

namespace VFAdapter {

namespace fw = SolidEdgeFramework;
namespace pt = SolidEdgePart;
namespace cs = SolidEdgeConstants;
namespace gm = SolidEdgeGeometry;
namespace fwp = SolidEdgeFrameworkSupport;
fw::ApplicationPtr SEAPP = nullptr;

std::string UnicodeToUtf8(const wchar_t *unicode) {
  int len;
  len = WideCharToMultiByte(CP_UTF8, 0, unicode, -1, NULL, 0, NULL, NULL);
  char *szUtf8 = (char *)malloc(len + 1);
  memset(szUtf8, 0, len + 1);
  WideCharToMultiByte(CP_UTF8, 0, unicode, -1, szUtf8, len, NULL, NULL);
  std::string strUtf8 = szUtf8;
  delete szUtf8;
  return strUtf8;
}

void DestroySafaArray(LPSAFEARRAY &psa) {
  if (psa) {
    SafeArrayDestroy(psa);
    psa = nullptr;
  }
}

void LogComError(std::string_view funcName, _com_error const &e) {
  std::string errMsg = UnicodeToUtf8(e.ErrorMessage());
  BOOST_LOG_TRIVIAL(error) << std::format("com错误:于函数{}处有误，错误原因:{}",
                                          funcName, errMsg);
}

// 全局临时显示对象容器
using TransientObjects = std::vector<LPDISPATCH>;
TransientObjects g_tempObjs;

vfHRESULT CreateSegment(int64_t &segId, DPoint3d const &pt1,
                        DPoint3d const &pt2) {
  try {
    pt::Sketch3DPtr pSketch = ApiUtils::GetFirstSketch3D();
    pt::Line3DPtr pLine = pSketch->GetLines3D()->Add(pt1[0], pt1[1], pt1[2],
                                                     pt2[0], pt2[1], pt2[2]);
    gm::EdgePtr pEdge = ApiUtils::EntityToGeomtry(pLine);
    if (nullptr == pEdge) {
      BOOST_LOG_TRIVIAL(error) << std::format(
          "查找线段对应拓扑有误，线段ID:{},点1:{},{},{},点2:{},{},{}",
          ApiUtils::GetFeatureID(pLine), pt1.x(), pt1.y(), pt1.z(), pt2.x(),
          pt2.y(), pt2.z());
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }
    segId = ApiUtils::GetEntityID(pEdge);
    if (segId == UINT64_MAX) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }
    return vf_Success;
  } catch (_com_error const &e) {
    LogComError(__func__, e);
    BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
    return vf_Failure;
  }
}

bool LinkTGCad(fw::ApplicationPtr &app) {
  HRESULT hr = app.GetActiveObject(L"SolidEdge.Application");

  if (FAILED(hr)) {
    hr = app.CreateInstance(L"SolidEdge.Application");
    app->PutVisible(true);
    app->PutInteractive(true);
  }
  return SUCCEEDED(hr);
}

vfHRESULT vfStartLinkWithCAD(int iCadType) {
  BOOST_LOG_TRIVIAL(info) << LogUtil::GetLogContent(__func__, iCadType);
  if (iCadType != TGC_CODE) {
    BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
    return vf_Failure;
  }

  bool rt = LinkTGCad(SEAPP);
  return (rt ? vf_Success : vf_Failure);
}

vfHRESULT vfGetEntityType(const char *lID, geom_entity_type &enType) {
  BOOST_LOG_TRIVIAL(info) << LogUtil::GetLogContent(__func__, lID);
  try {
    int64_t objId = std::stoull(lID);
    // int objType;
    // vfHRESULT status = ApiUtils::GetObjType(objType, lID);
    // if (vf_Success != status) {
    //   BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
    //   return vf_Failure;
    // }

    // enType = ApiUtils::ToEntityType(objType);

    // 现在的ID看个位可分辨类型
    // 1为点，2为线，3为面，4为体，5为链，6为壳
    int objType = objId % 10;
    switch (objType) {
    case 1:
      enType = vf_vertex;
      break;
    case 2:
      enType = vf_curve;
      break;
    case 3:
      enType = vf_face;
      break;
    case 4:
      enType = vf_body;
      break;
    case 5:
      enType = vf_curves;
      break;
    case 6:
      enType = vf_faces;
    }
  } catch (_com_error const &e) {
    LogComError(__func__, e);
    BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
    return vf_Failure;
  }

  return vf_Success;
}

vfHRESULT vfCreatePoint(double x, double y, double z, const char *&ptID) {
  try {
    BOOST_LOG_TRIVIAL(info) << LogUtil::GetLogContent(__func__, x, y, z);
    pt::PartDocumentPtr pPartDoc = SEAPP->GetActiveDocument();
    if (nullptr == pPartDoc) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    // 如无三维草图，则创建;否则在第一个三维草图上创建点
    pt::Sketch3DPtr pFstSketch = pPartDoc->GetSketches3D()->Add();
    ATL::CComSafeArray<double> pt = ApiUtils::ToComSafeArray(
        {x * MM_TO_COORD, y * MM_TO_COORD, z * MM_TO_COORD});
    pt::Points3DPtr pPts3d = pFstSketch->GetPoints3D();
    long ptCnt = pPts3d->GetCount();
    pt::Point3DPtr pPt3d = pPts3d->Add(3, pt.GetSafeArrayPtr());

    gm::VertexPtr pVtx = ApiUtils::EntityToGeomtry(pPt3d, ptCnt);
    int64_t entID = ApiUtils::GetEntityID(pVtx);
    if (UINT64_MAX == entID) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }
    // 问题，连续创建的点获得的是同一个持久化ID。
    // 因为其内部获得的对象是个点集。
    std::string entIDStr = std::to_string(entID);
    ptID = _strdup(entIDStr.c_str());
    BOOST_LOG_TRIVIAL(info) << LogUtil::GetLogContent(__func__, "ptID:", ptID);
    return vf_Success;
  } catch (_com_error const &e) {
    LogComError(__func__, e);
    BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
    return vf_Failure;
  }

  BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
  return vf_Failure;
}

vfHRESULT vfCurveCurveIntersection(const char *lCurveTag1,
                                   const char *lCurveTag2, double &x, double &y,
                                   double &z, const char *&ptID) {
  BOOST_LOG_TRIVIAL(info) << LogUtil::GetLogContent(__func__, lCurveTag1,
                                                    lCurveTag2);
  try {
    int64_t tag1 = std::stoull(lCurveTag1);
    int64_t tag2 = std::stoull(lCurveTag2);

    int64_t ptId = 0;
    vfHRESULT status =
        vfCurveCurveIntersection_old(tag1, tag2, x, y, z, true, ptId);
    if (vf_Success != status) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }
    std::string ptIdStr = std::to_string(ptId);
    ptID = _strdup(ptIdStr.c_str());
    return vf_Success;
  } catch (_com_error const &e) {
    BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
    return vf_Failure;
  }
}

vfHRESULT vfCurveCurveIntersection_old(int64_t lCurveTag1, int64_t lCurveTag2,
                                       double &x, double &y, double &z,
                                       bool bCreatePoint, int64_t &ptID) {
  // 已存在的两条曲线求交点;如有交点则返回交点，如有多个交点，则返回第一个。
  // 如要求在交点处创建点，则创建并返回这个点的ID
  try {
    IDispatchPtr pEdgeFst = ApiUtils::FindEntityByID(lCurveTag1);
    IDispatchPtr pEdgeScd = ApiUtils::FindEntityByID(lCurveTag2);

    pt::PartDocumentPtr pPartDoc = SEAPP->GetActiveDocument();
    if (pEdgeFst == nullptr || pEdgeScd == nullptr || pPartDoc == nullptr) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }
    pt::IntersectionPointPtr pIntersectPt =
        pPartDoc->GetConstructions()->GetIntersectionPoints()->Add(pEdgeFst,
                                                                   pEdgeScd);

    // drop之后，再算这两个物体的交点，总是计算不了，无论手动还是用代码

    // 获取交点特征此时的坐标
    gm::EdgesPtr pEdges = pIntersectPt->GetEdges(gm::igQueryAll);
    if (pEdges == nullptr || pEdges->GetCount() == 0) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    gm::EdgePtr pIntersectEdge = pEdges->Item(1l);
    gm::VertexPtr pVtxFst = pIntersectEdge->GetStartVertex();
    ATL::CComSafeArray<double> ptData(3);
    pVtxFst->GetPointData(ptData.GetSafeArrayPtr());
    x = ptData.GetAt(0);
    y = ptData.GetAt(1);
    z = ptData.GetAt(2);

    //  如不创建点，删除此特征?
    if (!bCreatePoint) {
      pIntersectPt->Delete();
      return vf_Success;
    }

    ptID = ApiUtils::GetEntityID(pVtxFst);
    if (ptID == UINT64_MAX) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }
    return vf_Success;
  } catch (_com_error const &e) {
    LogComError(__func__, e);
    BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
    return vf_Failure;
  }

  BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
  return vf_Failure;
}

vfHRESULT vfCurveSurfaceIntersection(const char *lCurveTag1,
                                     const char *lSurfaceTag1, double &x,
                                     double &y, double &z, const char *&ptID) {
  BOOST_LOG_TRIVIAL(info) << LogUtil::GetLogContent(__func__, lCurveTag1,
                                                    lSurfaceTag1);

  try {
    int64_t tag1 = std::stoull(lCurveTag1);
    int64_t tag2 = std::stoull(lSurfaceTag1);

    int64_t ptId = 0;
    vfHRESULT status =
        vfCurveSurfaceIntersection_old(tag1, tag2, x, y, z, true, ptId);
    if (vf_Success != status) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }
    std::string ptIdStr = std::to_string(ptId);
    ptID = _strdup(ptIdStr.c_str());
    return vf_Success;
  } catch (_com_error const &e) {
    BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
    return vf_Failure;
  }
}

vfHRESULT vfCurveSurfaceIntersection_old(int64_t lCurveTag1,
                                         int64_t lSurfaceTag1, double &x,
                                         double &y, double &z,
                                         bool bCreatePoint, int64_t &ptID) {
  BOOST_LOG_TRIVIAL(info) << __func__;
  // 已存在的一条曲线与曲面求交，如有交点则返回交点。如有多个交点，则返回第一个。
  // 如要求在交点处创建点，则创建点并返回此点的ID

  try {
    gm::FacePtr pFace = ApiUtils::FindEntityByID(lSurfaceTag1);
    gm::EdgePtr pEdge = ApiUtils::FindEntityByID(lCurveTag1);
    pt::PartDocumentPtr pPartDoc = SEAPP->GetActiveDocument();
    if (pFace == nullptr || pEdge == nullptr || pPartDoc == nullptr) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    // 参数转换
    pt::IntersectionPointsPtr pIntersectionPoints =
        pPartDoc->Constructions->GetIntersectionPoints();

    pt::IntersectionPointPtr pIntersectionPoint =
        pIntersectionPoints->Add(pEdge, pFace);
    gm::EdgesPtr pEdges = pIntersectionPoint->GetEdges(gm::igQueryAll);
    if (pEdges == nullptr || pEdges->GetCount() == 0) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    gm::EdgePtr pFstEdge = pEdges->Item(1l);
    ATL::CComSafeArray<double> ptSt(3);
    gm::VertexPtr pFstVtx = pFstEdge->GetStartVertex();
    pFstVtx->GetPointData(ptSt.GetSafeArrayPtr());
    x = ptSt.GetAt(0);
    y = ptSt.GetAt(1);
    z = ptSt.GetAt(2);

    // 如要求在交点处创建点，则创建并返回这个点的ID
    if (!bCreatePoint) {
      pIntersectionPoint->Delete();
      return vf_Success;
    }

    // 返回第一个点的ID
    ptID = ApiUtils::GetEntityID(pFstVtx);
    if (UINT64_MAX == ptID) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    return vf_Success;
  } catch (const _com_error &e) {
    LogComError(__func__, e);
    BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
    return vf_Failure;
  }

  BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
  return vf_Failure;
}

vfHRESULT FindEdgebarWindow(HWND &hwndDest) {
  HWND hwndParent = FindWindow(_T("EngineFrame"), nullptr);
  if (hwndParent == nullptr) {
    _tprintf(_T("未找到父窗口 EngineFrame\n"));
    BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
    return vf_Failure;
  }

  // 查找得所有WPF Page窗口
  std::unordered_set<HWND> wpfPageWindows;
  ApiUtils::FindWpfWindows(hwndParent, &wpfPageWindows);
  if (wpfPageWindows.empty()) {
    BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
    return vf_Failure;
  }

  // wfp窗口可能有点多，发起一个验证

  auto it = std::find_if(wpfPageWindows.begin(), wpfPageWindows.end(),
                         [&](HWND hwnd) -> bool {
                           return (SendMessage(hwnd, WM_CHECK, 11, 10) == 110);
                         });
  if (it == wpfPageWindows.end()) {
    BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
    return vf_Failure;
  }
  hwndDest = *it;
  return vf_Success;
};

vfHRESULT MsgSendAndReceive(std::string &msgReceived, IMessage const &pMsg) {

  HWND hwndDest;
  if (vf_Success != FindEdgebarWindow(hwndDest)) {
    BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
    return vf_Failure;
  }

  // 构造tcp服务端，返回端口号，并发送给插件
  boost::shared_ptr<TcpListen> pServer(new TcpListen());
  uint16_t port = pServer->GetPort();

  // 构造发送消息
  std::string msgStr = pMsg.ToJson();
  COPYDATASTRUCT cds;
  cds.dwData = 0;
  cds.cbData = msgStr.size();
  cds.lpData = msgStr.data();

  // 发送消息，启动命令，预期返回结果为1
  LRESULT res = SendMessage(hwndDest, WM_COPYDATA, port, (LPARAM)(&cds));
  if (res == 0) {
    BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
    return vf_Failure;
  }

  // 下述语句开始监听命令，并会阻塞当前线程
  pServer->StartListen(msgReceived);
  // 超时返回时，接收到的消息为空。
  return msgReceived.empty() ? vf_Failure : vf_Success;
}

vfHRESULT SelectByInteractive(SelReturnData &rtData, int geoType,
                              bool isSingleSelect, wchar_t *strMessage,
                              bool bObjectActionParadigmPossibility) {

  // 如果选择预选对象，且预选对象中有符合类型的对象，则直接返回
  if (bObjectActionParadigmPossibility &&
      ApiUtils::HasEntityInSelectionSet(rtData.ElementIds, geoType)) {
    rtData.errCode = 0;
    return vf_Success;
  }
  std::string receivedMsg;
  std::string promptMsgUtf8 = UnicodeToUtf8(strMessage);
  SelFilterArg selArg(geoType, promptMsgUtf8, isSingleSelect);
  if (vf_Success != MsgSendAndReceive(receivedMsg, selArg)) {
    BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
    return vf_Failure;
  }

  rtData = SelReturnData::FromJson(receivedMsg);
  return (rtData.errCode == 0) ? vf_Success : vf_Failure;
}

vfHRESULT vfSelectSingleByInteractive(geom_entity_type geoType, int64_t &tagID,
                                      wchar_t *strMessage,
                                      bool bObjectActionParadigmPossibility) {
  // 交互式根据筛选器选择一个拓扑对象，并返回他的ID
  // 如选择预先对象，且选择集中已有此类型的对象，则返回其ID
  try {
    BOOST_LOG_TRIVIAL(info)
        << LogUtil::GetLogContent(__func__, geoType, strMessage);
    SelReturnData rtData;
    vfHRESULT status = SelectByInteractive(rtData, geoType, true, strMessage,
                                           bObjectActionParadigmPossibility);
    if (status != vf_Success) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }
    if (!rtData.ElementIds.empty()) {
      tagID = rtData.ElementIds[0];
    }
    return vf_Success;
  } catch (_com_error const &e) {
    LogComError(__func__, e);
    BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
    return vf_Failure;
  }

  BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
  return vf_Failure;
}

vfHRESULT vfSelectByTag(const char *tagID) {
  BOOST_LOG_TRIVIAL(info) << LogUtil::GetLogContent(__func__, tagID);
  try {

    IDispatchPtr pEnt = ApiUtils::FindEntityByID(tagID);
    if (pEnt == nullptr) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }
    SEAPP->GetActiveSelectSet()->Add(pEnt);
    return vf_Success;
  } catch (_com_error const &e) {
    LogComError(__func__, e);
  }
  BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
  return vf_Failure;
}

vfHRESULT vfClearSeletion() {
  try {
    BOOST_LOG_TRIVIAL(info) << __func__;
    SEAPP->GetActiveSelectSet()->RemoveAll();
    return vf_Success;
  } catch (_com_error const &e) {
    LogComError(__func__, e);
    BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
    return vf_Failure;
  }
  BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
  return vf_Failure;
}

vfHRESULT vfOffsetCurve(const char *curveID1, const char *surfaceID, double dis,
                        const char *&curveID2) {
  BOOST_LOG_TRIVIAL(info) << LogUtil::GetLogContent(__func__, curveID1,
                                                    surfaceID, dis);
  try {
    pt::PartDocumentPtr pPartDoc = SEAPP->GetActiveDocument();
    gm::EdgePtr pEdge = ApiUtils::FindEntityByID(curveID1);
    gm::FacePtr pFace = ApiUtils::FindEntityByID(surfaceID);
    if (pEdge == nullptr || pFace == nullptr || pPartDoc == nullptr) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    pt::SurfaceOffsetCurvesPtr pOffsetCurves =
        pPartDoc->GetConstructions()->GetSurfaceOffsetCurves();
    pt::SurfaceOffsetCurvePtr pOffsetCurve = pOffsetCurves->Add(
        pEdge, pFace, pt::FeaturePropertyConstants::igLeft, dis);

    gm::EdgesPtr pEdges = pOffsetCurve->GetEdges(gm::igQueryAll);
    if (pEdges == nullptr || pEdges->GetCount() == 0) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    gm::EdgePtr pFstEdge = pEdges->Item(1l);
    curveID2 = ApiUtils::GetEntityIDString(pFstEdge);
    if (nullptr == curveID2) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }
    return vf_Success;
  } catch (const _com_error &e) {
    LogComError(__func__, e);
    BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
    return vf_Failure;
  }

  return vf_Success;
}

vfHRESULT vfCreateCurveBySurfaceIntersection(const char *surfaceID1,
                                             const char *surfaceID2,
                                             const char *&curveID) {
  BOOST_LOG_TRIVIAL(info) << LogUtil::GetLogContent(__func__, surfaceID1,
                                                    surfaceID2);
  try {
    int64_t idSurface1 = std::stoull(surfaceID1);
    int64_t idSurface2 = std::stoull(surfaceID2);
    int64_t idCurve = 0;
    if (vf_Success != vfCreateCurveBySurfaceIntersection_old(
                          idSurface1, idSurface2, idCurve)) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }
    std::string idCurveStr = std::to_string(idCurve);
    curveID = _strdup(idCurveStr.c_str());
    return vf_Success;
  } catch (_com_error const &e) {
    BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
    return vf_Failure;
  }
}

vfHRESULT vfCreateCurveBySurfaceIntersection_old(int64_t surfaceID1,
                                                 int64_t surfaceID2,
                                                 int64_t &curveID) {
  try {
    gm::FacePtr pFaceFst = ApiUtils::FindEntityByID(surfaceID1);
    gm::FacePtr pFaceScd = ApiUtils::FindEntityByID(surfaceID2);

    pt::PartDocumentPtr pPartDoc = SEAPP->GetActiveDocument();
    if (pFaceFst == nullptr || pFaceScd == nullptr || pPartDoc == nullptr) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    pt::IntersectionCurvePtr pIntersectCv =
        pPartDoc->GetConstructions()->GetIntersectionCurves()->Add(pFaceFst,
                                                                   pFaceScd);

    // 返回第一条交线ID
    gm::EdgesPtr pEdges = pIntersectCv->GetEdges(gm::igQueryAll);
    if (pEdges == nullptr || pEdges->GetCount() == 0) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    gm::EdgePtr pFstEdge = pEdges->Item(1l);
    curveID = ApiUtils::GetEntityID(pFstEdge);
    // pIntersectCv->ed
    return vf_Success;
  } catch (_com_error const &e) {
    LogComError(__func__, e);
    BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
    return vf_Failure;
  }

  BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
  return vf_Failure;
}

vfHRESULT vfOffsetSurface(const char *surfaceID1, const char *pointID,
                          double dis, const char *&surfaceID2) {
  BOOST_LOG_TRIVIAL(info) << LogUtil::GetLogContent(
      __func__, "surfaceID1:", surfaceID1, "pointId:", pointID, "dis:", dis);

  try {
    int64_t idSurface1 = std::stoull(surfaceID1);
    int64_t idPoint = std::stoull(pointID);
    pt::PartDocumentPtr pPartDoc = SEAPP->GetActiveDocument();
    gm::FacePtr pFace = ApiUtils::FindEntityByID(idSurface1);
    gm::VertexPtr pVertex = ApiUtils::FindEntityByID(idPoint);
    if (nullptr == pPartDoc || nullptr == pFace || nullptr == pVertex) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    // 取得曲面的法向与，根据顶点判定要偏移是与法向同侧还是法向反侧
    ATL::CComSafeArray<double> minParaArr(2);
    ATL::CComSafeArray<double> maxParaArr(2);
    pFace->GetParamRange(minParaArr.GetSafeArrayPtr(),
                         maxParaArr.GetSafeArrayPtr());
    double paraU = 0.5 * (minParaArr[0] + maxParaArr[0]);
    double paraV = 0.5 * (minParaArr[1] + maxParaArr[1]);

    ATL::CComSafeArray<double> centerPtPara =
        ApiUtils::ToComSafeArray({paraU, paraV});
    ATL::CComSafeArray<double> normal(3);
    pFace->GetNormal(1, centerPtPara.GetSafeArrayPtr(),
                     normal.GetSafeArrayPtr());
    ATL::CComSafeArray<double> ptCenter(3);
    pFace->GetPointAtParam(1, centerPtPara.GetSafeArrayPtr(),
                           ptCenter.GetSafeArrayPtr());

    ATL::CComSafeArray<double> ptPoint;
    pVertex->GetPointData(ptPoint.GetSafeArrayPtr());

    // 判定给定空间点是否与法向同向
    DVec3d dirNormal(normal[0], normal[1], normal[2]);
    DVec3d dirPoint(ptPoint[0] - ptCenter[0], ptPoint[1] - ptCenter[1],
                    ptPoint[2] - ptCenter[2]);
    double dot = dirPoint.dot(dirNormal);
    pt::FeaturePropertyConstants eSide;
    if (dot > 0) {
      // 同向
      eSide = pt::FeaturePropertyConstants::igLeft;
    } else {
      eSide = pt::FeaturePropertyConstants::igRight;
    }

    // 添加等距曲面
    pt::OffsetSurfacesPtr pOffsetSurfaces =
        pPartDoc->Constructions->OffsetSurfaces;
    pt::OffsetSurfacePtr pOffsetSurface =
        pOffsetSurfaces->Add(eSide, dis, pFace);
    // 返回拓扑面的ID
    gm::FacesPtr pFaces = pOffsetSurface->Faces[gm::igQueryAll];
    pFace = pFaces->Item(1);
    int64_t idResFace = ApiUtils::GetEntityID(pFace);
    std::string idSurfaceStr = std::to_string(idResFace);
    surfaceID2 = _strdup(idSurfaceStr.c_str());
  } catch (const _com_error &e) {
    LogComError(__func__, e);
    BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
    return vf_Failure;
  }

  return vf_Success;
}

vfHRESULT vfOffsetSurface_old(int64_t surfaceID1, double dis,
                              int64_t &surfaceID2) {
  // 通过添加 等距曲面的方法OffsetSurfaces -> add
  // 缺点 新增的等距曲面是原本曲面的引用对象

  try {
    pt::PartDocumentPtr pPartDoc = SEAPP->GetActiveDocument();
    gm::FacePtr pFace = ApiUtils::FindEntityByID(surfaceID1);
    if (nullptr == pPartDoc || nullptr == pFace) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }
    // 添加等距曲面
    pt::OffsetSurfacesPtr pOffsetSurfaces =
        pPartDoc->Constructions->OffsetSurfaces;
    pt::FeaturePropertyConstants eSide = pt::FeaturePropertyConstants::igLeft;
    pt::OffsetSurfacePtr pOffsetSurface =
        pOffsetSurfaces->Add(eSide, dis, pFace);
    // 返回拓扑面的ID
    gm::FacesPtr pFaces = pOffsetSurface->Faces[gm::igQueryAll];
    pFace = pFaces->Item(1);
    surfaceID2 = ApiUtils::GetEntityID(pFace);
  } catch (const _com_error &e) {
    LogComError(__func__, e);
    BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
    return vf_Failure;
  }

  return vf_Success;
}

vfHRESULT vfCreateSurfaceByLoft(const char **curveIDs, int num,
                                const char *&surfaceID) {
  BOOST_LOG_TRIVIAL(info) << __func__;
  // 通过添加 扫描曲面的方法SweptSurfaces -> add
  // 缺点 多个截面曲面创建的曲面需要有个路径，这个路径应该如何定义

  try {
    pt::PartDocumentPtr pPartDoc = SEAPP->GetActiveDocument();
    // 参数保护
    if (nullptr == curveIDs || nullptr == pPartDoc) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }
    // 数据转换
    ATL::CComSafeArray<IDispatch *> curves(num);
    ATL::CComSafeArray<IDispatch *> originsCurves(num); // 曲线原点

    for (int i = 0; i < num; i++) {

      gm::EdgePtr pEdge = ApiUtils::FindEntityByID(curveIDs[i]);
      if (nullptr == pEdge) {
        BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
        return vf_Failure;
      }
      // 曲线原点
      originsCurves.SetAt(i, pEdge->GetStartVertex());
      curves.SetAt(i, pEdge);
    }
    ATL::CComSafeArray<IDispatch *> guideCurves(num); // 指导路径

    pt::BlueSurfsPtr pBlueSurfs = pPartDoc->Constructions->GetBlueSurfs();
    pt::BlueSurfPtr pBlueSur = pBlueSurfs->Add(
        num, curves.GetSafeArrayPtr(), originsCurves.GetSafeArrayPtr(),
        pt::FeaturePropertyConstants::igNatural, 0.0,
        pt::FeaturePropertyConstants::igNatural, 0.0, 0,
        guideCurves.GetSafeArrayPtr(), pt::FeaturePropertyConstants::igNatural,
        0.0, pt::FeaturePropertyConstants::igNatural, 0.0, true, false);

    // 状态检查
    VARIANT vDescription;
    if (nullptr == pBlueSur ||
        pt::igFeatureFailed == pBlueSur->GetStatus(&vDescription)) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }
    // 返回参数
    gm::FacesPtr pFaces = pBlueSur->Faces[gm::igQueryAll];
    gm::FacePtr pFace = pFaces->Item(1);
    surfaceID = ApiUtils::GetEntityIDString(pFace);

  } catch (const _com_error &e) {
    LogComError(__func__, e);
    BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
    return vf_Failure;
  }

  return vf_Success;
}

vfHRESULT vfSurfaceMerge(const char **surfaceIDs, int num,
                         const char *&newSurfaceID) {
  BOOST_LOG_TRIVIAL(info) << __func__;
  // 合并接口不支持 曲面体

  // 暂时用 缝合曲面来替换看一下效果

  try {
    pt::PartDocumentPtr pPartDoc = SEAPP->GetActiveDocument();
    if (nullptr == pPartDoc || nullptr == surfaceIDs || 0 == num) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }
    // 参数设置
    std::vector<int64_t> vecSurfaceId;

    for (int i = 0; i < num; ++i) {
      const char *idStr = surfaceIDs[i];
      int64_t idSurface = std::stoull(idStr);
      vecSurfaceId.push_back(idSurface);
    }
    std::vector<LPDISPATCH> vecFaces;
    for (auto id : vecSurfaceId) {
      IDispatchPtr pDisp = ApiUtils::FindEntityByID(id);
      gm::FacesPtr pFaces = pDisp;
      if (pFaces == nullptr) {
        pDisp->AddRef();
        vecFaces.push_back(pDisp);
      } else {
        for (long i = 0; i < pFaces->GetCount(); i++) {
          IDispatchPtr pSubFace = pFaces->Item(i + 1);
          pSubFace->AddRef();
          vecFaces.push_back(pSubFace);
        }
      }
    }
    num = (int)vecFaces.size();
    ATL::CComSafeArray<IDispatch *> aSurfaces(num);
    for (int i = 0; i < num; i++) {
      aSurfaces[i] = vecFaces[i];
    }

    // 添加一个缝合对象
    pt::StitchSurfacePtr pStitchSurface =
        pPartDoc->Constructions->StitchSurfaces->Add(
            num, aSurfaces.GetSafeArrayPtr());
    // 返回新的实体的ID
    // pt::ConstructionModelPtr pConModel = pStitchSurface->Parent;
    // gm::BodyPtr pBody = pConModel->GetBody();
    gm::FacesPtr pFaces = pStitchSurface->Faces[gm::igQueryAll];
    newSurfaceID = ApiUtils::GetEntityIDString(pFaces);

  } catch (const _com_error &e) {
    LogComError(__func__, e);
    BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
    return vf_Failure;
  }
  return vf_Success;
}

vfHRESULT vfCreateSurfaceByFill_old(int64_t *curveIDs, int num,
                                    int64_t &surfaceID) {
  BOOST_LOG_TRIVIAL(info) << __func__;
  try {
    pt::PartDocumentPtr pPartDoc = SEAPP->GetActiveDocument();
    // 参数保护
    if (nullptr == curveIDs || nullptr == pPartDoc) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    // 参数转换
    // Edge 或者 CurvePtr都可以填充曲面
    ATL::CComSafeArray<IDispatch *> arrEdges(num);
    for (long count = 0; count < num; ++count) {
      gm::EdgePtr pEdge = ApiUtils::FindEntityByID(*curveIDs);
      if (nullptr == pEdge) {
        BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
        return vf_Failure;
      }
      arrEdges.SetAt(count, pEdge);
      curveIDs++;
    }

    // 添加扫描曲面
    pt::SurfaceByBoundariesPtr pSurfaceByBoundaries =
        pPartDoc->Constructions->SurfaceByBoundaries;
    pt::SurfaceByBoundaryPtr pSurfaceByBoundary = pSurfaceByBoundaries->Add(
        arrEdges.GetCount(), arrEdges.GetSafeArrayPtr());

    // 返回拓扑面的ID
    gm::FacesPtr pFaces = pSurfaceByBoundary->Faces[gm::igQueryAll];
    gm::FacePtr pFace = pFaces->Item(1);
    surfaceID = ApiUtils::GetEntityID(pFace);
  } catch (const _com_error &e) {
    LogComError(__func__, e);
    BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
    return vf_Failure;
  }

  return vf_Success;
}

vfHRESULT vfSurfaceArea(const char *surfaceID, double &dArea) {
  BOOST_LOG_TRIVIAL(info) << LogUtil::GetLogContent(__func__,
                                                    "surfaceID:", surfaceID);
  try {
    int64_t surfaceID2 = std::stoull(surfaceID);
    IDispatchPtr pSurface = ApiUtils::FindEntityByID(surfaceID2);
    if (nullptr == surfaceID) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    geom_entity_type type = ApiUtils::GetGeomType(pSurface);
    if (geom_entity_type::vf_face != type &&
        geom_entity_type::vf_faces != type) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    if (geom_entity_type::vf_face == type) {
      // 单个面
      vfSurfaceArea_old(surfaceID2, dArea);

    } else {
      gm::FacesPtr pFaces = pSurface;
      dArea = 0;
      for (long i = 0; i < pFaces->Count; ++i) {
        gm::FacePtr pFace = pFaces->Item(i + 1);
        double singleArea = pFace->GetArea();
        dArea += singleArea;
      }
    }
    // 转化为平方毫米
    dArea = dArea * COORD_TO_MM * COORD_TO_MM;
    return vf_Success;
  } catch (const _com_error &e) {
    BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
    return vf_Failure;
  }
}

vfHRESULT vfSurfaceArea_old(int64_t surfaceID, double &dArea) {
  // 通过曲面对象获取 Faces对象，遍历Face做面积求和
  try {
    pt::PartDocumentPtr pPartDoc = SEAPP->GetActiveDocument();
    dArea = 0.0;
    // 数据转换
    gm::FacePtr pFace = ApiUtils::FindEntityByID(surfaceID);
    if (nullptr == pFace || nullptr == pPartDoc) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }
    dArea = pFace->GetArea();
  } catch (const _com_error &e) {
    LogComError(__func__, e);
    BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
    return vf_Failure;
  }

  return vf_Success;
}

vfHRESULT vfSurfacePerimeter(const char *surfaceID, double &dPerimeter) {
  BOOST_LOG_TRIVIAL(info) << LogUtil::GetLogContent(__func__,
                                                    "surfaceID:", surfaceID);
  try {
    int64_t surfaceID2 = std::stoull(surfaceID);
    IDispatchPtr pSurface = ApiUtils::FindEntityByID(surfaceID2);
    if (nullptr == surfaceID) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    geom_entity_type type = ApiUtils::GetGeomType(pSurface);
    if (geom_entity_type::vf_face != type &&
        geom_entity_type::vf_faces != type) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    if (geom_entity_type::vf_face == type) {
      // 单个面
      vfSurfacePerimeter_old(surfaceID2, dPerimeter);
    } else {
      // 多个面，必是壳
      gm::FacesPtr pFaces = pSurface;
      if (pFaces->GetCount() == 0) {
        BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
        return vf_Failure;
      }
      gm::FacePtr pFace = pFaces->Item(1l);
      assert(pFace);
      gm::ShellPtr pShell = pFace->GetShell();
      assert(pShell);
      long edgeCnt = -1;
      LPSAFEARRAY pEdgesArr = nullptr;
      pShell->GetNonStitchedEdges(&edgeCnt, &pEdgesArr);
      if (edgeCnt > 0) {
        ATL::CComSafeArray<IDispatch *> arrEdges;
        arrEdges.Attach(pEdgesArr);

        dPerimeter = 0;
        ATL::CComSafeArray<double> params(2);
        for (long i = 0; i < edgeCnt; ++i) {
          IDispatch *pDisp = arrEdges[i];
          gm::EdgesPtr pEdges = pDisp;
          int64_t edgesId = ApiUtils::GetEntityID(pEdges);
          for (long j = 0; j < pEdges->GetCount(); ++j) {
            gm::EdgePtr pEdge = pEdges->Item(j + 1);
            pEdge->GetParamExtents(&params[0], &params[1]);
            double dTmpLength = 0;
            pEdge->GetLengthAtParam(params[0], params[1], &dTmpLength);
            dPerimeter += dTmpLength;
          }
        }
      }
    }
    // 转化为毫米
    dPerimeter = dPerimeter * COORD_TO_MM;
    return vf_Success;
  } catch (const _com_error &e) {
    BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
    return vf_Failure;
  }
}

vfHRESULT vfSurfacePerimeter_old(int64_t surfaceID, double &dPerimeter) {
  BOOST_LOG_TRIVIAL(info) << __func__;
  // 简单方法，获取曲面的Edges对象，遍历Edge做长度求和
  try {
    pt::PartDocumentPtr pPartDoc = SEAPP->GetActiveDocument();
    // 数据转换
    gm::FacePtr pFace = ApiUtils::FindEntityByID(surfaceID);
    if (nullptr == pFace || nullptr == pPartDoc) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }
    dPerimeter = 0.0;
    // 取边
    gm::EdgesPtr pEdges = pFace->Edges;
    for (long j = 1; j <= pEdges->Count; ++j) {
      gm::EdgePtr pEdge = pEdges->Item(j);

      double minParam, maxParam, dLen;
      pEdge->GetParamExtents(&minParam, &maxParam);
      pEdge->GetLengthAtParam(minParam, maxParam, &dLen);
      dPerimeter += dLen;
    }

  } catch (const _com_error &e) {
    LogComError(__func__, e);
    BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
    return vf_Failure;
  }

  return vf_Success;
}

vfHRESULT vfSurfaceWeight(const char *surfaceID, double SurfaceDensity,
                          double &dWeight) {

  BOOST_LOG_TRIVIAL(info) << LogUtil::GetLogContent(
      __func__, "surfaceID:", surfaceID, "SurfaceDensity:", SurfaceDensity);
  try {
    int64_t surfaceID2 = std::stoull(surfaceID);
    return vfSurfaceWeight_old(surfaceID2, SurfaceDensity, dWeight);
  } catch (const _com_error &e) {
    BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
    return vf_Failure;
  }
}

vfHRESULT vfSurfaceWeight_old(int64_t surfaceID, double surfaceDensity,
                              double &dWeight) {
  // 没有直接的方法，目前简单公式化实现

  try {
    gm::FacePtr pFace = ApiUtils::FindEntityByID(surfaceID);
    gm::BodyPtr pBody = pFace->GetBody();
    if (nullptr == pFace || nullptr == pBody) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    double density = surfaceDensity;
    double accuracy = 1.0; // 精度到毫米
    //
    ATL::CComSafeArray<double> centerOfVolumn(3);
    ATL::CComSafeArray<double> centerOfGravity(3);
    ATL::CComSafeArray<double> axes(9);
    ATL::CComSafeArray<double> globalMomentsOfInteria(6);
    ATL::CComSafeArray<double> principalMomentsOfInteria(3);
    ATL::CComSafeArray<double> radiiOfGyration(3);

    double volume;
    double area;
    double mass;
    double relativeAccuracyAchieved;
    int status;

    pBody->ComputePhysicalProperties(
        density, accuracy, &volume, &area, &mass,
        centerOfGravity.GetSafeArrayPtr(), centerOfVolumn.GetSafeArrayPtr(),
        globalMomentsOfInteria.GetSafeArrayPtr(),
        principalMomentsOfInteria.GetSafeArrayPtr(), axes.GetSafeArrayPtr(),
        radiiOfGyration.GetSafeArrayPtr(), &relativeAccuracyAchieved, &status);
    dWeight = mass;
  } catch (const _com_error &e) {
    LogComError(__func__, e);
    BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
    return vf_Failure;
  }

  return vf_Success;
}

vfHRESULT vfSelectMul(geom_entity_type geoType, int &ptCnt, int64_t **pPts,
                      wchar_t *strMessage,
                      bool bObjectActionParadigmPossibility) {
  std::vector<int64_t> tagIDs;
  vfHRESULT status = vfSelectMultiyInteractive(
      geoType, tagIDs, strMessage, bObjectActionParadigmPossibility);
  ptCnt = tagIDs.size();
  if (vf_Success == status) {
    int64_t *data = new int64_t[ptCnt];
    memcpy(data, tagIDs.data(), ptCnt * sizeof(int64_t));
    *pPts = data;
  }

  return status;
}
vfHRESULT vfSelectMultiyInteractive(int geoType, std::vector<int64_t> &tagIDs,
                                    wchar_t *strMessage,
                                    bool bObjectActionParadigmPossibility) {

  try {
    SelReturnData rtData;
    vfHRESULT status = SelectByInteractive(rtData, geoType, false, strMessage,
                                           bObjectActionParadigmPossibility);
    if (status != vf_Success) {
      return status;
    }
    tagIDs = rtData.ElementIds;
    pt::PartDocumentPtr pPartDoc = SEAPP->GetActiveDocument();
    if (nullptr == pPartDoc) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }
    pPartDoc->GetSelectSet()->RemoveAll();
    return vf_Success;
  } catch (const _com_error &e) {
    LogComError(__func__, e);
    BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
    return vf_Failure;
  }
}

vfHRESULT vfMirrorSurface(const char *surfaceID1, const char *mirrorPlaneID,
                          bool bReserved, const char *&surfaceID2) {

  try {
    BOOST_LOG_TRIVIAL(info) << LogUtil::GetLogContent(
        __func__, "surfaceId1:", surfaceID1, "mirrorPlaneID:", mirrorPlaneID,
        "bReserved:", bReserved);
    pt::PartDocumentPtr pPartDoc = SEAPP->GetActiveDocument();
    // 数据转换
    gm::FacePtr pFace = ApiUtils::FindEntityByID(surfaceID1);
    pt::RefPlanePtr pPlane = ApiUtils::FindFeatureByID(mirrorPlaneID);

    if (nullptr == pFace || nullptr == pPlane || nullptr == pPartDoc) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    // pt::RefPlanePtr pPlane = pPartDoc->GetRefPlanes()->AddParallelByDistance(
    //     pFacePlane, 0.0,
    //     SolidEdgePart::ReferenceElementConstants::igNormalSide);

    // 参数解析
    ATL::CComSafeArray<IDispatch *> comSafeArray(1);
    comSafeArray.SetAt(0, pFace);

    // 添加一个镜像曲面
    pt::MirrorCopyGeometriesPtr pMirrorCopyGeometries =
        pPartDoc->Constructions->MirrorCopyGeometries;

    bool RemoveOriginal = !bReserved;
    pt::MirrorCopyGeometryPtr pMirrorCopyGeometry =
        pMirrorCopyGeometries->AddByRemoveOriginal(
            comSafeArray.GetCount(), comSafeArray.GetSafeArrayPtr(), pPlane,
            RemoveOriginal);
    // 返回曲面的拓扑ID
    gm::FacesPtr pFaces = pMirrorCopyGeometry->Faces[gm::igQueryAll];
    pFace = pFaces->Item(1);
    surfaceID2 = ApiUtils::GetEntityIDString(pFace);

  } catch (const _com_error &e) {
    LogComError(__func__, e);
    BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
    return vf_Failure;
  }

  return vf_Success;
}

vfHRESULT vfCreateCurveOnSurfaceByTwoPoints(const double *X1, const double *Y1,
                                            const double *Z1, const double *X2,
                                            const double *Y2, const double *Z2,
                                            const char *SurfaceID) {

  const char **rtIds = nullptr;
  return vfCreateCurveOnSurfaceByTwoPoints3(X1, Y1, Z1, X2, Y2, Z2, SurfaceID,
                                            rtIds);
}

vfHRESULT vfCreateCurveOnSurfaceByTwoPoints_old(int64_t pointID1,
                                                int64_t pointID2,
                                                int64_t surfaceID,
                                                int64_t &curveID) {
  // 通过沿面的方式去实现 ContourCurves  ->add 方法
  BOOST_LOG_TRIVIAL(info) << __func__;

  try {
    pt::PartDocumentPtr pPartDoc = SEAPP->GetActiveDocument();
    // 数据转换
    gm::FacePtr pFace = ApiUtils::FindEntityByID(surfaceID); // 曲面

    gm::VertexPtr pVertexOne = ApiUtils::FindEntityByID(pointID1);
    gm::VertexPtr pVertexTwo = ApiUtils::FindEntityByID(pointID2);
    if (nullptr == pFace || nullptr == pVertexOne || nullptr == pVertexTwo ||
        nullptr == pPartDoc) {
      BOOST_LOG_TRIVIAL(info) << "pFace or pVertexOne or pVertexTwo or "
                                 "pPartDoc is nullptr";
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }
    // 获取点的信息
    long NumPoints = 2;
    ATL::CComSafeArray<double> PointSet(6);
    ATL::CComSafeArray<double> aPoint(3);
    ATL::CComSafeArray<double> bPoint(3);
    pVertexOne->GetPointData(aPoint.GetSafeArrayPtr());
    for (int i = 0; i < 3; ++i) {

      PointSet.SetAt(i, aPoint.GetAt(i));
    }

    pVertexTwo->GetPointData(bPoint.GetSafeArrayPtr());
    for (int i = 3; i < 6; ++i) {
      PointSet.SetAt(i, bPoint.GetAt(i - 3));
    }

    pt::Sketch3DPtr pFstSketch3d = ApiUtils::GetFirstSketch3D();
    if (nullptr == pFstSketch3d) {
      BOOST_LOG_TRIVIAL(info) << "pFstSketch3d is nullptr";
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }
    pt::BSplineCurves3DPtr pSpCvs = pFstSketch3d->GetBSplineCurves3D();
    LPSAFEARRAY geodesicCvs;
    pSpCvs->AddByGeodesic(pFace, aPoint.GetSafeArrayPtr(),
                          bPoint.GetSafeArrayPtr(), VARIANT_TRUE, &geodesicCvs);

    //   gm::EdgePtr pEdge = ApiUtils::EntityToGeomtry(pSplineCv3d);
    //  curveID = ApiUtils::GetEntityID(pEdge);

#if 0
  // 沿面方式(轮廓)
    // 绘制曲线的面
    long NumFacesOnWhichCurveNeedsToBeDrawn = 1;
    ATL::CComSafeArray<IDispatch *> FacesOnWhichTheCurveIsDarwn(1);
    FacesOnWhichTheCurveIsDarwn[0] = pFace;

    ATL::CComSafeArray<IDispatch *> FaceOrEdgeOnWhichNodePointsAreDefined(2);
    FaceOrEdgeOnWhichNodePointsAreDefined[0] = pFace;
    FaceOrEdgeOnWhichNodePointsAreDefined[1] = pFace;
    // 添加一个沿面对象
    pt::ContourCurvesPtr pContourCurves =
        pPartDoc->Constructions->ContourCurves;
    pt::ContourCurvePtr pContourCurve = pContourCurves->Add(
        NumFacesOnWhichCurveNeedsToBeDrawn,
        FacesOnWhichTheCurveIsDarwn.GetSafeArrayPtr(), NumPoints,
        PointSet.GetSafeArrayPtr(),
        FaceOrEdgeOnWhichNodePointsAreDefined.GetSafeArrayPtr(), true);
    // 返回曲线的拓扑ID
    gm::EdgesPtr pEdges = pContourCurve->Edges[gm::igQueryAll];
    gm::EdgePtr pEdge = pEdges->Item(1);
    curveID = ApiUtils::GetEntityID(pEdge);
#endif
  } catch (const _com_error &e) {
    LogComError(__func__, e);
    BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
    return vf_Failure;
  }

  return vf_Success;
}

vfHRESULT vfMirrorCurve(const char *curveID1, const char *mirrorPlane,
                        bool bReserved, const char *&curveID2) {
  BOOST_LOG_TRIVIAL(info) << LogUtil::GetLogContent(
      __func__, "curveId1:", curveID1, "mirrorPlane:", mirrorPlane,
      "bReserved:", bReserved);
  try {
    pt::PartDocumentPtr pPartDoc = SEAPP->GetActiveDocument();
    // 数据转换

    gm::EdgePtr pEdge = ApiUtils::FindEntityByID(curveID1);
    pt::RefPlanePtr pPlane = ApiUtils::FindFeatureByID(mirrorPlane);

    if (nullptr == pEdge || nullptr == pPlane || nullptr == pPartDoc) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }
    // pt::RefPlanePtr pPlane = pPartDoc->GetRefPlanes()->AddParallelByDistance(
    //     pFacePlane, 0.0,
    //     SolidEdgePart::ReferenceElementConstants::igNormalSide);

    // 添加一个镜像曲线
    pt::MirrorCopyGeometriesPtr pMirrorCopyGeometries =
        pPartDoc->Constructions->MirrorCopyGeometries;
    ATL::CComSafeArray<IDispatch *> comSafeArray(1);
    comSafeArray.SetAt(0, pEdge);

    bool RemoveOriginal = !bReserved;
    pt::MirrorCopyGeometryPtr pMirrorCopyGeometry =
        pMirrorCopyGeometries->AddByRemoveOriginal(
            comSafeArray.GetCount(), comSafeArray.GetSafeArrayPtr(), pPlane,
            RemoveOriginal);

    // 返回曲线的拓扑边
    gm::EdgesPtr pEdges = pMirrorCopyGeometry->Edges[gm::igQueryAll];
    pEdge = pEdges->Item(1);
    curveID2 = ApiUtils::GetEntityIDString(pEdge);

  } catch (const _com_error &e) {
    LogComError(__func__, e);
    BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
    return vf_Failure;
  }

  return vf_Success;
}

vfHRESULT vfCreateCurveByProjection(const char *curveID1, const char *surfaceID,
                                    const char *&curveID2) {
  BOOST_LOG_TRIVIAL(info) << LogUtil::GetLogContent(
      __func__, "curveID1:", curveID1, "surfaceID:", surfaceID);
  // ProjectCurves -> add
  // 通过投影创建投影曲线
  try {
    pt::PartDocumentPtr pPartDoc = SEAPP->GetActiveDocument();
    // 数据转换
    gm::EdgePtr pEdge = ApiUtils::FindEntityByID(curveID1);
    gm::FacePtr pFace = ApiUtils::FindEntityByID(surfaceID);
    if (nullptr == pEdge || nullptr == pFace || nullptr == pPartDoc) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }
    pt::RefPlanePtr pRefplane = pPartDoc->GetRefPlanes()->AddParallelByDistance(
        pFace, 0.0, pt::ReferenceElementConstants::igNormalSide);

    // 添加一个投影曲线
    pt::ProjectCurvesPtr pProjectCurves =
        pPartDoc->Constructions->ProjectCurves;
    pt::ProjectCurvePtr pProjectCurve = pProjectCurves->Add(
        pEdge, pFace, pRefplane, pt::FeaturePropertyConstants::igBoth,
        pt::FeaturePropertyConstants::igProjectOptionProject);
    // 返回投影曲线拓朴ID
    gm::EdgesPtr pEdges = pProjectCurve->Edges[gm::igQueryAll];
    pEdge = pEdges->Item(1);
    curveID2 = ApiUtils::GetEntityIDString(pEdge);

  } catch (const _com_error &e) {
    LogComError(__func__, e);
    BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
    return vf_Failure;
  }

  return vf_Success;
}

vfHRESULT vfCreateSplineCurve(const char **pointIDs, int num, bool bControlPt,
                              const char *&curveID) {
  BOOST_LOG_TRIVIAL(info) << __func__;
  // 采用样条曲线的创建方法
  try {
    pt::PartDocumentPtr pPartDoc = SEAPP->GetActiveDocument();
    // 参数保护
    if (num < 2 || nullptr == pointIDs || nullptr == pPartDoc) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }
    // 数据转换
    SolidEdgePart::Sketch3DPtr pSketch3D = ApiUtils::GetFirstSketch3D();
    pt::BSplineCurves3DPtr pBSplineCurves3d = pSketch3D->GetBSplineCurves3D();

    // 参数
    ATL::CComSafeArray<double> result(num * 3);
    for (int i = 0; i < num; ++i) {

      SolidEdgeGeometry::VertexPtr pVertex =
          ApiUtils::FindEntityByID(*pointIDs);
      if (nullptr == pVertex) {
        BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
        return vf_Failure;
      }

      ATL::CComSafeArray<double> pt(3);
      pVertex->GetPointData(pt.GetSafeArrayPtr());

      for (int j = 0; j < 3; ++j) {

        result.SetAt(i * 3 + j, pt.GetAt(j));
      }
      pointIDs++;
    }

    // 三维样条曲线
    if (bControlPt) {

      pBSplineCurves3d->AddByControlPoints(num, result.GetSafeArrayPtr());
    } else {

      pBSplineCurves3d->AddByPoints(num, result.GetSafeArrayPtr(), false);
    }

  } catch (const _com_error &e) {
    LogComError(__func__, e);
    BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
    return vf_Failure;
  }

  return vf_Success;
}

vfHRESULT vfCreateBodyWithThickedSurfaceOld(int64_t surfaceID, double thickness,
                                            double *direction,
                                            int64_t &bodyID) {

  try {
    pt::PartDocumentPtr pPartDoc = SEAPP->GetActiveDocument();

    // 数据转换
    gm::FacePtr pFace = ApiUtils::FindEntityByID(surfaceID);
    if (nullptr == pPartDoc || nullptr == pFace) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }
    ATL::CComSafeArray<IDispatch *> aFaces(1);
    aFaces.SetAt(0, pFace);
    pt::FeaturePropertyConstants eSide = pt::FeaturePropertyConstants::igLeft;

    // 添加一个加厚体
    if (pPartDoc->Models->Count == 0) {
      pt::ModelPtr pModel = pPartDoc->Models->AddThickenFeature(
          eSide, thickness, 1, aFaces.GetSafeArrayPtr());
      gm::BodyPtr pBody = pModel->Body;

      bodyID = ApiUtils::GetEntityID(pBody);
    } else {

      // 先添加一个体
      int count = pPartDoc->Models->Count;
      CString sName = _T("");
      sName.Format(_T("设计体_%d"), count + 1);
      auto res = pPartDoc->Models->AddBody(pt::igPartType, _bstr_t(sName));
      if (res == S_OK) {

        pt::ThickenPtr pThicken = pPartDoc->Models->Item(count)->Thickens->Add(
            eSide, thickness, 1, aFaces.GetSafeArrayPtr());
        pt::ModelPtr pModel = pThicken->Parent;
        gm::BodyPtr pBody = pModel->Body;
        bodyID = ApiUtils::GetEntityID(pBody);
      }
    }
  } catch (const _com_error &e) {
    LogComError(__func__, e);
    BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
    return vf_Failure;
  }

  return vf_Success;
}

vfHRESULT vfCreateBodyWithThickedSurface(const char *surfaceID,
                                         double thickness,
                                         const char *&bodyID) {
  BOOST_LOG_TRIVIAL(info) << LogUtil::GetLogContent(
      __func__, "surfaceID:", surfaceID, "thickness:", thickness);

  int64_t idSurface = std::stoull(surfaceID);
  int64_t tResBodyId;
  vfHRESULT status = vfCreateBodyWithThickedSurfaceOld(idSurface, thickness,
                                                       nullptr, tResBodyId);
  std::string strBodyID = std::to_string(tResBodyId);
  bodyID = _strdup(strBodyID.c_str());
  return status;
#if 0
  try {

    pt::PartDocumentPtr pDoc = SEAPP->GetActiveDocument();

    int64_t idOne, idTwo;
    vfHRESULT res = vfCreatePoint(0.0, 0.0, 0.0, idOne);
    res = vfCreatePoint(direction[0], direction[1], direction[2], idTwo);
    gm::VertexPtr pStartVertex = ApiUtils::FindEntityByID(idOne);
    gm::VertexPtr pEndVertex = ApiUtils::FindEntityByID(idTwo);
    // 面
    gm::FacePtr pFace = ApiUtils::FindEntityByID(surfaceID);
    if (nullptr == direction || nullptr == pFace) {
            BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    // // 起始点
    // gm::VerticesPtr pVertices = pFace->Vertices;    // // 向量点
    // gm::VertexPtr pStartVertex = pVertices->Item(1);

    // ATL::CComSafeArray<double> aPoints(3);
    // pStartVertex->GetPointData(aPoints.GetSafeArrayPtr());
    // aPoints.SetAt(0, aPoints.GetAt(0) + direction[0]);
    // aPoints.SetAt(1, aPoints.GetAt(1) + direction[1]);
    // aPoints.SetAt(2, aPoints.GetAt(2) + direction[2]);

    // pt::Points3DPtr pPoints3D = pSketch3d->GetPoints3D();
    // long count = pPoints3D->GetCount();
    // pt::Point3DPtr pEndPoint = pPoints3D->Add(3, aPoints.GetSafeArrayPtr());

    // gm::VertexPtr pEndVertex =
    //     pDoc->MapSketch3dGeometryToCurve(pEndPoint, count);

    // 参数设置
    long numFace = 1l;
    ATL::CComSafeArray<IDispatch *> aFaces(numFace);
    aFaces[0] = pFace;

    pt::FaceMoveConstants faceMoveType = pt::igFaceMoveAlong2PointVector;
    pt::MoveConnectedFaceTypes faceConnectType =
        pt::seMoveConnectedFaceTypeLift;
    pt::MovePrecedenceConstants facePrecedenceType =
        pt::igSelectSetMovePrecedence;
    pt::FaceMoveConstants blendRecreation = pt::igFaceMoveIgnoreBlends;
    pt::FaceMoveConstants alongOrRev = pt::igFaceMoveAlongVector;
    pt::FaceMoveConstants offsetAlongOrRev = pt::igFaceMoveOffsetAlongVector;

    long numRules = 14l;
    ATL::CComSafeArray<int> aLiveRules(numRules);
    aLiveRules.SetAt(0, pt::LiveRulesConstants::igConcentricLiveRule);
    aLiveRules.SetAt(1, pt::LiveRulesConstants::igCoplanarLiveRule);
    aLiveRules.SetAt(2, pt::LiveRulesConstants::igTangentEdgeLiveRule);
    aLiveRules.SetAt(3, pt::LiveRulesConstants::igTangentTouchingLiveRule);
    aLiveRules.SetAt(4, pt::LiveRulesConstants::igParallelLiveRule);
    aLiveRules.SetAt(5, pt::LiveRulesConstants::igPerpendicularLiveRule);
    aLiveRules.SetAt(6, pt::LiveRulesConstants::igSymmetricXYLiveRule);
    aLiveRules.SetAt(7, pt::LiveRulesConstants::igSymmetricYZLiveRule);
    aLiveRules.SetAt(8, pt::LiveRulesConstants::igSymmetricZXLiveRule);
    aLiveRules.SetAt(9, pt::LiveRulesConstants::igMaintainRadiusLiveRule);
    aLiveRules.SetAt(10, pt::LiveRulesConstants::igOrthoLockingLiveRule);
    aLiveRules.SetAt(11, pt::LiveRulesConstants::igCoplanarAxesAboutXLiveRule);
    aLiveRules.SetAt(12, pt::LiveRulesConstants::igCoplanarAxesAboutYLiveRule);
    aLiveRules.SetAt(13, pt::LiveRulesConstants::igCoplanarAxesAboutZLiveRule);

    ATL::CComSafeArray<VARIANT_BOOL> aLiveRulesOff(numRules);
    for (int i = 0; i < numRules; ++i) {
      aLiveRulesOff.SetAt(i, false);
    }

    pt::ConstructionModelPtr pModel = pDoc->GetConstructions()->Item(1);
    pt::FaceMovePtr pFaceMove = pModel->FaceMoves->AddEx(
        numFace, aFaces.GetSafeArrayPtr(), faceMoveType, faceConnectType,
        facePrecedenceType, numRules, aLiveRules.GetSafeArrayPtr(),
        aLiveRulesOff.GetSafeArrayPtr(), blendRecreation, pStartVertex,
        pEndVertex, nullptr, nullptr, nullptr, alongOrRev, pStartVertex, 0.01,
        nullptr, 0, offsetAlongOrRev);

    // 状态检查
    // VARIANT vDescription;
    // if (nullptr == pFaceMove ||
    //     pt::igFeatureFailed == pFaceMove->GetStatus(&vDescription)) {
    //         BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    // }

    // 找出顶面的边
    std::map<int, bool> mEdges;
    gm::FacesPtr pFaces = pFaceMove->GetFaces(gm::igQueryAll);
    for (long i = 1; i <= pFaces->Count; ++i) {
      gm::FacePtr pTempFace = pFaces->Item(i);
      long idFace = ApiUtils::GetEntityID(pTempFace);
      gm::EdgesPtr pEdges = pTempFace->GetEdges();
      for (long j = 1; j <= pEdges->Count; ++j) {
        IDispatchPtr t = pEdges->Item(j);
        gm::EdgePtr pEdge = pEdges->Item(j);
        long idEdge = ApiUtils::GetEntityID(pEdge);
        if (0 == mEdges.count(idEdge)) {
          mEdges.insert(std::make_pair(idEdge, true));
        } else {
          mEdges[idEdge] = false;
        }
      }
    }

    // // 构造填充面
    int sort = 0;
    for (int i = 0; i < mEdges.size(); ++i) {
      if (mEdges[i]) {
        ++sort;
      }
    }

    ATL::CComSafeArray<IDispatch *> aEdges(sort);
    sort = 0;
    std::for_each(mEdges.begin(), mEdges.end(),
                  [&aEdges, &sort](const std::pair<int, bool> &v) {
                    if (v.second) {
                      gm::EdgePtr pEdge = ApiUtils::FindEntityByID(v.first);
                      aEdges.SetAt(sort, pEdge);
                      sort++;
                    }
                  });

    // 添加扫描曲面
    pt::SurfaceByBoundariesPtr pSurfaceByBoundaries =
        pDoc->Constructions->SurfaceByBoundaries;
    pt::SurfaceByBoundaryPtr pSurfaceByBoundary =
        pSurfaceByBoundaries->Add(aEdges.GetCount(), aEdges.GetSafeArrayPtr());

    // if (nullptr == pSurfaceByBoundary ||
    //     pt::igFeatureFailed == pSurfaceByBoundary->GetStatus(&vDescription))
    //     {
    //         BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    // }

    // 曲面缝合构造体
    int faceCount = pFaces->Count;

    ATL::CComSafeArray<IDispatch *> aMovefaces(faceCount);
    for (int i = 1; i <= pFaces->Count; ++i) {
      gm::FacePtr pTempFace = pFaces->Item(i);
      aMovefaces.SetAt(i - 1, pTempFace);
    }

    pt::CopySurfacesPtr pCopySurfaces = pDoc->Constructions->CopySurfaces;
    pt::CopySurfacePtr pCopySurface =
        pCopySurfaces->Add(faceCount, aMovefaces.GetSafeArrayPtr());
    // 状态检查
    // if (nullptr == pCopySurface ||
    //     pt::igFeatureFailed == pCopySurface->GetStatus(&vDescription)) {
    //         BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    // }

    ATL::CComSafeArray<IDispatch *> aSurfaces(2);
    // gm::FacePtr pFillFace = ApiUtils::FindEntityByID(idFillSurface);
    aSurfaces.SetAt(0, pSurfaceByBoundary);
    aSurfaces.SetAt(1, pCopySurface);

    // 添加一个缝合体对象
    pt::StitchSurfacePtr pStitchSurface =
        pDoc->Constructions->StitchSurfaces->AddEx(
            2, aSurfaces.GetSafeArrayPtr(), VARIANT_TRUE);
    // 状态检查
    // if (nullptr == pStitchSurface ||
    //     pt::igFeatureFailed == pStitchSurface->GetStatus(&vDescription)) {
    //         BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    // }

    pt::ConstructionModelPtr pConstrucModel = pStitchSurface->Parent;
    gm::BodyPtr pBody = pConstrucModel->GetBody();
    int64_t idBody = ApiUtils::GetEntityID(pBody);

  } catch (const _com_error &e) {
    LogComError(__func__, e);
          BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
  }
  return vf_Success;
#endif
}

vfHRESULT vfCreateGravityOfSurface(const char *surfaceID, double &x, double &y,
                                   double &z) {
  BOOST_LOG_TRIVIAL(info) << LogUtil::GetLogContent(__func__,
                                                    "surfaceID:", surfaceID);

  try {
    pt::PartDocumentPtr pPartDoc = SEAPP->GetActiveDocument();
    if (nullptr == pPartDoc || nullptr == surfaceID || surfaceID[0] == '\0') {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    int64_t idSurface = std::stoull(surfaceID);
    IDispatchPtr pFacOrFaces = ApiUtils::FindEntityByID(idSurface);
    if (nullptr == pFacOrFaces) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }
    ATL::CComSafeArray<double> gravity(3);
    pPartDoc->GetFaceOrFacesGravity(pFacOrFaces, gravity.GetSafeArrayPtr());
    x = gravity[0];
    y = gravity[1];
    z = gravity[2];
    return vf_Success;
  } catch (const _com_error &e) {
    BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
    return vf_Failure;
  }
}

vfHRESULT vfCreateGravityOfSurface_old(int64_t surfaceID, double *dGravity) {
  BOOST_LOG_TRIVIAL(info) << __func__;

  try {
    pt::PartDocumentPtr pPartDoc = SEAPP->GetActiveDocument();
    // 数据转换
    gm::FacePtr pFace = ApiUtils::FindEntityByID(surfaceID);
    if (nullptr == pPartDoc || nullptr == pFace || nullptr == dGravity) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    ATL::CComSafeArray<double> graviaty(3);
    pFace->GetGravity(graviaty.GetSafeArrayPtr());
    for (int i = 0; i < 3; ++i) {

      *dGravity = graviaty[i];
      dGravity++;
    }

  } catch (_com_error) {
    BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
    return vf_Failure;
  }

  return vf_Success;
}

vfHRESULT vfTestPointOnCurve(const char *pointID, const char *curveID,
                             bool &bOn) {
  BOOST_LOG_TRIVIAL(info) << LogUtil::GetLogContent(
      __func__, "pointID:", pointID, "curveID:", curveID);
  try {
    int64_t idPoint = std::stoull(pointID);
    int64_t idCurve = std::stoull(curveID);
    vfHRESULT status = vfTestPointOnCurve_old(idPoint, idCurve, bOn);
    if (vf_Success != status) {
      return status;
    }
    return vf_Success;
  } catch (const _com_error &e) {
    BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
    return vf_Failure;
  }
}

vfHRESULT vfTestPointOnCurve_old(int64_t pointID, int64_t curveID, bool &bOn) {
  try {
    pt::PartDocumentPtr pPartDoc = SEAPP->GetActiveDocument();
    // 数据转换
    gm::VertexPtr pVertex = ApiUtils::FindEntityByID(pointID);
    gm::EdgePtr pEdge = ApiUtils::FindEntityByID(curveID);

    if (nullptr == pVertex || nullptr == pEdge || nullptr == pPartDoc) {

      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    // 取点
    ATL::CComSafeArray<double> aPoint(3);
    pVertex->GetPointData(aPoint.GetSafeArrayPtr());

    VARIANT_BOOL on = false;
    pEdge->IsPointOn(aPoint.GetSafeArrayPtr(), &on);
    bOn = on;

  } catch (const _com_error &e) {
    LogComError(__func__, e);
    BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
    return vf_Failure;
  }
  return vf_Success;
}

vfHRESULT vfTestPointOnSurface2(double x, double y, double z,
                                const char *surfaceID, int &result) {
  try {
    BOOST_LOG_TRIVIAL(info) << LogUtil::GetLogContent(
        __func__, "x:", x, "y:", y, "z:", z, "surfaceID:", surfaceID);

    int64_t idSurface = std::stoull(surfaceID);
    IDispatchPtr pFaceOrFaces = ApiUtils::FindEntityByID(idSurface);
    if (nullptr == pFaceOrFaces) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    geom_entity_type geoType = ApiUtils::GetGeomType(pFaceOrFaces);
    if (geom_entity_type::vf_faces != geoType &&
        geom_entity_type::vf_face != geoType) {
      // 输入参数错误, 只能是面或面组
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    x *= MM_TO_COORD;
    y *= MM_TO_COORD;
    z *= MM_TO_COORD;
    ATL::CComSafeArray<double> pointData = ApiUtils::ToComSafeArray({x, y, z});
    long bOn;
    ATL::CComSafeArray<double> uv(2);
    ATL::CComSafeArray<double> closePt(3);
    double closestDis;
    // 首先假定在面外
    result = 0;
    if (geom_entity_type::vf_faces == geoType) {
      gm::FacesPtr pFaces = pFaceOrFaces;
      for (long i = 0; i < pFaces->GetCount(); i++) {
        gm::FacePtr pFace = pFaces->Item(i + 1);
        pFace->IsPointOn(pointData.GetSafeArrayPtr(), &bOn,
                         uv.GetSafeArrayPtr(), &closestDis,
                         closePt.GetSafeArrayPtr());
        if (bOn) {
          // 如在任一面内或任一面边界内,
          result = bOn;
          return vf_Success;
        }
      }
    } else {
      // 单个面时
      gm::FacePtr pFace = pFaceOrFaces;

      pFace->IsPointOn(pointData.GetSafeArrayPtr(), &bOn, uv.GetSafeArrayPtr(),
                       &closestDis, closePt.GetSafeArrayPtr());
      result = bOn;
    }
    return vf_Success;
  } catch (const _com_error &e) {
    LogComError(__func__, e);
    BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
    return vf_Failure;
  }
}

vfHRESULT vfTestPointOnSurface(const char *pointID, const char *surfaceID,
                               int &result) {
  try {
    BOOST_LOG_TRIVIAL(info) << LogUtil::GetLogContent(
        __func__, "PointID:", pointID, "SurfaceID:", surfaceID);
    //  result
    // int &
    //     result：点相对于曲面的三种情况（若返回0，则点在面外；若返回1，则点在面内；若返回2，则点在面的边界上）；
    int64_t idPoint = std::stoull(pointID);
    int64_t idSurface = std::stoull(surfaceID);
    long bOn;
    vfHRESULT status = vfTestPointOnSurface_old(idPoint, idSurface, bOn);
    if (vf_Success != status) {
      return status;
    }
    result = bOn;
    return vf_Success;
  } catch (_com_error const &e) {
    BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
    return vf_Failure;
  }
}

//!
//! @brief 计算一点离面或面组的最近点
//!
//! @param pSurface
//! @param X1
//! @param Y1
//! @param Z1
//! @return vfHRESULT
//!
vfHRESULT GetMinimumDistancePointCoordinates(IDispatchPtr const &pSurface,
                                             double &X1, double &Y1, double &Z1,
                                             long &bOn) {
  if (nullptr == pSurface) {
    return vf_Failure;
  }
  try {
    pt::PartDocumentPtr pPartDoc = SEAPP->GetActiveDocument();
    if (nullptr == pPartDoc) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    ATL::CComSafeArray<double> pt = ApiUtils::ToComSafeArray({X1, Y1, Z1});
    ATL::CComSafeArray<double> ptClosest(3);
    HRESULT hr = pPartDoc->GetMinumDistancePoint(pSurface.GetInterfacePtr(),
                                                 pt.GetSafeArrayPtr(),
                                                 ptClosest.GetSafeArrayPtr());
    if (hr != S_OK) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }
    X1 = ptClosest[0];
    Y1 = ptClosest[1];
    Z1 = ptClosest[2];
    return vf_Success;
  } catch (_com_error const &e) {
    LogComError(__func__, e);
    return vf_Failure;
  }
}

vfHRESULT vfTestPointOnSurface_old(int64_t pointID, int64_t surfaceID,
                                   long &bOn) {
  try {
    pt::PartDocumentPtr pPartDoc = SEAPP->GetActiveDocument();
    // 数据转换
    gm::VertexPtr pVertex = ApiUtils::FindEntityByID(pointID);
    IDispatchPtr pFace = ApiUtils::FindEntityByID(surfaceID);

    if (nullptr == pVertex || nullptr == pFace || nullptr == pPartDoc) {

      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    // 取点
    ATL::CComSafeArray<double> aPoint(3);
    pVertex->GetPointData(aPoint.GetSafeArrayPtr());
    return GetMinimumDistancePointCoordinates(pFace, aPoint[0], aPoint[1],
                                              aPoint[2], bOn);

  } catch (const _com_error &e) {
    LogComError(__func__, e);
    BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
    return vf_Failure;
  }
  return vf_Success;
}

// 三期

vfHRESULT vfMousePtOnSurface(const char *surfaceID, double &x, double &y,
                             double &z) {
  try {
    BOOST_LOG_TRIVIAL(info)
        << LogUtil::GetLogContent(__func__, "surfaceID:", surfaceID);
    // 发送消息给插件，启动在指定面上点击命令,并等待消息
    IDispatchPtr pFaceOrFaces = ApiUtils::FindEntityByID(surfaceID);
    if (nullptr == pFaceOrFaces) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    int64_t idSurface = std::stoull(surfaceID);

    GetMousePointArg arg(idSurface);
    std::string msgReceived;
    if (vf_Success != MsgSendAndReceive(msgReceived, arg)) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    // 解析json，取得鼠标点中的空间点
    GetMousePointReturnData retData =
        GetMousePointReturnData::FromJson(msgReceived);
    if (retData.ErrorCode != 0) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }
    x = retData.X[0];
    y = retData.Y[0];
    z = retData.Z[0];

    // 尝试创建此空间点
    // int64_t ptSpaceId;
    // vfCreatePoint(x, y, z, ptSpaceId);

    // 获取面上离此空间点最近点
    long bOn = -1;
    if (vf_Success !=
        GetMinimumDistancePointCoordinates(pFaceOrFaces, x, y, z, bOn)) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    x *= COORD_TO_MM;
    y *= COORD_TO_MM;
    z *= COORD_TO_MM;
    return vf_Success;
  } catch (const _com_error &e) {
    LogComError(__func__, e);
    BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
    return vf_Failure;
  }
}
template <typename T> _variant_t CreateVariant(T *t, VARENUM em) {
  // 暂时支持数量为一的一维数组
  SAFEARRAYBOUND bound = {1, 0};
  SAFEARRAY *pSafeArry = SafeArrayCreate(em, 1, &bound);
  SafeArrayPutElement(pSafeArry, &bound.lLbound, t);
  VARIANT var;
  VariantInit(&var);
  var.vt = VT_ARRAY | em;
  var.parray = pSafeArry;
  return _variant_t(var);
}
vfHRESULT vfMirrorObject(const char *objectID1, const char *mirrorPlane,
                         bool bReserved, const char *&objectID2) {
  try {
    BOOST_LOG_TRIVIAL(info) << LogUtil::GetLogContent(
        __func__, "objectID1:", objectID1, "mirrorPlane:", mirrorPlane,
        "bReserved:", bReserved);
    IDispatchPtr pFeature = ApiUtils::FindFeatureByID(objectID1);
    pt::RefPlanePtr pPlane = ApiUtils::FindFeatureByID(mirrorPlane);
    if (nullptr == pFeature || nullptr == pPlane) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }
    _variant_t parentVar;
    HRESULT hr = ApiUtils::GetDispProperty(parentVar, pFeature, L"Parent");
    if (FAILED(hr)) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    IDispatchPtr pParent = parentVar.pdispVal;

    pt::ModelPtr pModel = pParent;
    if (nullptr == pModel) {
      pt::PartDocumentPtr pPartDoc = SEAPP->GetActiveDocument();
      pt::ConstructionsPtr pConstructions = pPartDoc->GetConstructions();
      pt::MirrorCopyGeometriesPtr pMiGeoms =
          pConstructions->GetMirrorCopyGeometries();
      pt::ConstructionModelPtr pConstructionModel = pParent;
      pt::ConstructionsPtr pConstructParent = pParent;
      if (nullptr != pConstructionModel) {
        // 镜像有body的构造体特征，如拉伸曲面之类
        gm::BodyPtr pBody = pConstructionModel->GetBody();
        ATL::CComSafeArray<IDispatch *> aBodys(1);
        aBodys.SetAt(0, pBody.GetInterfacePtr());
        pt::MirrorCopyGeometryPtr pMiFeature = pMiGeoms->AddByRemoveOriginal(
            1, aBodys.GetSafeArrayPtr(), pPlane, !bReserved);
        int64_t miFeatureId = ApiUtils::GetFeatureID(pMiFeature);
        if (UINT64_MAX == miFeatureId) {
          BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
          return vf_Failure;
        }
        std::string miFeatureIdStr = std::to_string(miFeatureId);
        objectID2 = _strdup(miFeatureIdStr.c_str());
      } else if (nullptr != pConstructParent) {
        // 镜像有
        _variant_t varRes;
        hr = ApiUtils::GetEdges(varRes, (int)gm::igQueryAll, pFeature);
        if (FAILED(hr)) {
          BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
          return vf_Failure;
        }
        gm::EdgesPtr pEdges = varRes.pdispVal;
        if (pEdges->GetCount() == 0) {
          BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
          return vf_Failure;
        }
        gm::EdgePtr pEdge = pEdges->Item(1l);
        gm::BodyPtr pBody = pEdge->GetBody();
        ATL::CComSafeArray<IDispatch *> aBodys(1);
        aBodys.SetAt(0, pBody.GetInterfacePtr());
        pt::MirrorCopyGeometryPtr pMiFeature =
            pMiGeoms->Add(1, aBodys.GetSafeArrayPtr(), pPlane);
        int64_t miFeatureId = ApiUtils::GetFeatureID(pMiFeature);
        if (UINT64_MAX == miFeatureId) {
          BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
          return vf_Failure;
        }
        std::string miFeatureIdStr = std::to_string(miFeatureId);
        objectID2 = _strdup(miFeatureIdStr.c_str());
      }
      return vf_Success;
    } else {

      pt::MirrorCopiesPtr pMiCopies = pModel->GetMirrorCopies();
      _variant_t varFeatures = CreateVariant<IDispatch>(pFeature, VT_DISPATCH);
      pt::MirrorCopyPtr pMiFeature = pMiCopies->Add(pPlane, 1l, varFeatures);
      if (nullptr == pMiFeature) {
        BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
        return vf_Failure;
      }
      int64_t miFeatureId = ApiUtils::GetFeatureID(pMiFeature);
      if (UINT64_MAX == miFeatureId) {
        BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
        return vf_Failure;
      }
      std::string miFeatureIdStr = std::to_string(miFeatureId);
      objectID2 = _strdup(miFeatureIdStr.c_str());
      if (!bReserved) {

        // todo 如不保留，则删除原有特征
      }

      return vf_Success;
    }

  } catch (const _com_error &e) {
    LogComError(__func__, e);
    BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
    return vf_Failure;
  }
}

vfHRESULT vfCreateCurveByMultiCurves(const char **curveIDs, int num,
                                     bool bReserved, const char *&curveID) {
  try {
    BOOST_LOG_TRIVIAL(info) << __func__;
    if (nullptr == curveIDs || num < 1) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }
    pt::PartDocumentPtr pPartDoc = SEAPP->GetActiveDocument();
    if (nullptr == pPartDoc) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }
    ATL::CComSafeArray<IDispatch *> edgeArr(num);
    for (int i = 0; i < num; ++i) {
      int64_t edgeId = std::stoull(curveIDs[i]);
      gm::EdgePtr pEdge = ApiUtils::FindEntityByID(edgeId);
      if (nullptr == pEdge) {
        BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
        return vf_Failure;
      }
      edgeArr[i] = pEdge.GetInterfacePtr();
    }

    pt::DerivedCurvesPtr pDerivedCvs =
        pPartDoc->GetConstructions()->GetDerivedCurves();

    pt::DerivedCurvePtr pCompCv =
        pDerivedCvs->Add(num, edgeArr.GetSafeArrayPtr(),
                         pt::DerivedCurveTypeConstants::igDCComposite);
    if (nullptr == pCompCv) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    int64_t id = ApiUtils::GetFeatureID(pCompCv);
    std::string strId = std::to_string(id);
    curveID = _strdup(strId.c_str());

    if (!bReserved) {
      for (int i = 0; i < num; i++) {
        vfRemoveTargetObject(curveIDs[i]);
      }
    }
    return vf_Success;
  } catch (const _com_error &e) {
    LogComError(__func__, e);
    BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
    return vf_Failure;
  }
}

vfHRESULT vfCurveTrim(const char *curveID, const char *objTrimingID,
                      const char *&curveID2) {
  try {
    BOOST_LOG_TRIVIAL(info) << LogUtil::GetLogContent(
        __func__, "curveID:", curveID, "objTrimingID:", objTrimingID);

    // 计算交点
    double x, y, z;
    int64_t ptIntersectId;
    int64_t idCurve = std::stoull(curveID);
    int64_t idTriming = std::stoull(objTrimingID);

    vfHRESULT status = vfCurveCurveIntersection_old(idCurve, idTriming, x, y, z,
                                                    false, ptIntersectId);
    if (status != vf_Success) {
      return status;
    }

    gm::EdgePtr pCurveToTrim = ApiUtils::FindEntityByID(curveID);
    if (nullptr == pCurveToTrim) {
      return status;
    }

    int64_t cvId = ApiUtils::GetEdgeSegmentID(pCurveToTrim);
    IDispatchPtr pCv = ApiUtils::FindFeatureByID(cvId);
    if (nullptr == pCv) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }
    pt::Sketch3DPtr pSketch3d = ApiUtils::GetParent(pCv);
    if (nullptr == pCv) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }
    if (FAILED(pSketch3d->TrimByPoint(pCv, x, y, z))) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }
    // id 并未改变
    curveID2 = curveID;

    return vf_Success;
  } catch (const _com_error &e) {
    LogComError(__func__, e);
    BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
    return vf_Failure;
  }
}

vfHRESULT vfCreatePointOnCurve(const char *curveID, double u, double &x,
                               double &y, double &z, const char *&pointID) {
  try {
    BOOST_LOG_TRIVIAL(info) << LogUtil::GetLogContent(__func__, "u:", u);
    if (u < 0 || u > 1) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }
    std::string curveIDStr(curveID);
    int64_t curveId = std::stoll(curveIDStr);
    gm::EdgePtr pEdge = ApiUtils::FindEntityByID(curveId);
    if (nullptr == pEdge) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    double lowPara, highPara;
    if (FAILED(pEdge->GetParamExtents(&lowPara, &highPara))) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }
    double actualU = u * (lowPara + highPara);
    ATL::CComSafeArray<double> paArr(1);
    paArr.SetAt(0, actualU);
    ATL::CComSafeArray<double> ptRes(3);

    HRESULT hr = pEdge->GetPointAtParam(
        paArr.GetCount(), paArr.GetSafeArrayPtr(), ptRes.GetSafeArrayPtr());
    if (FAILED(hr)) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }
    x = ptRes[0];
    y = ptRes[1];
    z = ptRes[2];
    vfHRESULT status = vfCreatePoint(x, y, z, pointID);
    return status;
  } catch (const _com_error &e) {
    LogComError(__func__, e);
    BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
    return vf_Failure;
  }
}

vfHRESULT vfCreatePointOnSurface(const char *surfaceID, double u, double v,
                                 double &x, double &y, double &z,
                                 const char *&pointID) {
  try {
    BOOST_LOG_TRIVIAL(info) << LogUtil::GetLogContent(
        __func__, "surfaceID:", surfaceID, "u:", u, "v:", v);
    // 预期u，v均在[0,1]区间内
    if (u < 0 || u > 1 || v < 0 || v > 1) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    std::string surfaceIDStr(surfaceID);
    int64_t surfaceId = std::stoll(surfaceIDStr);

    gm::FacePtr pFace = ApiUtils::FindEntityByID(surfaceId);
    if (nullptr == pFace) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }
    LPSAFEARRAY pMiniParas = nullptr;
    LPSAFEARRAY pMaxParas = nullptr;
    HRESULT hr = pFace->GetParamRange(&pMiniParas, &pMaxParas);
    if (FAILED(hr)) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }
    ATL::CComSafeArray<double> miniParaArr;
    ATL::CComSafeArray<double> maxParaArr;
    miniParaArr.Attach(pMiniParas);
    maxParaArr.Attach(pMaxParas);

    double actualU = u * (miniParaArr[0] + maxParaArr[0]);
    double actualV = v * (miniParaArr[1] + maxParaArr[1]);

    ATL::CComSafeArray<double> paArr(2);
    paArr.SetAt(0, actualU);
    paArr.SetAt(1, actualV);
    ATL::CComSafeArray<double> ptRes(3);
    if (FAILED(pFace->GetPointAtParam(1, paArr.GetSafeArrayPtr(),
                                      ptRes.GetSafeArrayPtr()))) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }
    x = ptRes[0];
    y = ptRes[1];
    z = ptRes[2];
    vfHRESULT status = vfCreatePoint(x, y, z, pointID);
    return status;
  } catch (const _com_error &e) {
    LogComError(__func__, e);
    BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
    return vf_Failure;
  }
}

// 方程曲线坐标类型
enum FunctionCurveCoordType {
  Cartesian,   // 笛卡尔坐标
  Spherical,   // 球坐标
  Cylindrical, // 圆柱坐标
  Radial,      // 放射坐标
  Ridge,       // 脊坐标
};

vfHRESULT vfCreateFunctionCurve(int cType, wchar_t *strExpression1,
                                wchar_t *strExpression2,
                                wchar_t *strExpression3, const char *&curveID) {
  try {
    BOOST_LOG_TRIVIAL(info),
        LogUtil::GetLogContent(__func__, "cType:", cType,
                               "strExpression1:", strExpression1,
                               "strExpression2:", strExpression2,
                               "strExpression3:", strExpression3);
    // 坐标转换
    std::wstring exprX;
    std::wstring exprY;
    std::wstring exprZ;

    FunctionCurveCoordType coordType = (FunctionCurveCoordType)cType;
    switch (coordType) {
    case Cartesian: {
      // 笛卡尔坐标
      exprX = strExpression1;
      exprY = strExpression2;
      exprZ = strExpression3;
      break;
    }
    case Spherical: {
      // 球坐标
      // x= r*sin(theta)*cos(phi)
      exprX = std::format(L"{0}*sin({1})cos({2})", strExpression1,
                          strExpression2, strExpression3);
      // y = r*sin(theta)*sin(phi)
      exprY = std::format(L"{0}*sin({1})sin({2})", strExpression1,
                          strExpression2, strExpression3);
      // z = r*cos(theta)
      exprZ = std::format(L"{0}*cos({1})", strExpression1, strExpression2);
      break;
    }
    case Cylindrical: {
      // 圆柱坐标
      // x = p* cos(thea)
      exprX = std::format(L"{0}*cos({1})", strExpression1, strExpression2);
      // y = p*sin(thea)
      exprY = std::format(L"{0}*sin({1})", strExpression1, strExpression2);
      // z = z
      exprZ = strExpression3;
      break;
    }
    case Radial: {
      // todo
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
      break;
    }
    case Ridge: {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
      break;
    }
    default: {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
      break;
    }
    }

    // 生成方程曲线
    // 每个方向
    pt::PartDocumentPtr pPartDoc = SEAPP->GetActiveDocument();
    pt::Sketches3DPtr pSketches = pPartDoc->GetSketches3D();
    pt::Sketch3DPtr pFstSketch = pSketches->Add();
    if (nullptr == pFstSketch) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }
    _variant_t varExprX = exprX.c_str();
    _variant_t varExprY = exprY.c_str();
    _variant_t varExprZ = exprZ.c_str();
    _variant_t paraSt = 0.0;
    _variant_t paraEd = 1.0;
    pt::EquationDrivenCurveErrorCode errCode;
    pt::BSplineCurve3DPtr pCv = pFstSketch->AddEquationDrivenCurve(
        varExprX, varExprY, varExprZ, paraSt, paraEd, &errCode);
    if (errCode != pt::EquationDrivenCurveErrorCode::
                       seEquationDrivenCurveErrorCodeNoError ||
        nullptr == pCv) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    // 返回方程曲线的ID
    gm::EdgePtr pEdge = ApiUtils::EntityToGeomtry(pCv);
    if (nullptr == pEdge) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }
    curveID = ApiUtils::GetEntityIDString(pEdge);
    if (curveID == nullptr) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    return vf_Success;
  } catch (const _com_error &e) {
    LogComError(__func__, e);
    BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
    return vf_Failure;
  }
}

vfHRESULT vfChangeTheColorLineWidth(const char *objectID, int32_t *lineWidth,
                                    int32_t *colorR, int32_t *colorG,
                                    int32_t *colorB) {
  try {

    BOOST_LOG_TRIVIAL(info)
        << LogUtil::GetLogContent(__func__, "objectID:", objectID);

    int64_t idObj = std::stoull(objectID);
    HRESULT hr = ApiUtils::ThickenAndHighlight({idObj});
    return (SUCCEEDED(hr) ? vf_Success : vf_Failure);
  } catch (const _com_error &e) {
    LogComError(__func__, e);
    BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
    return vf_Failure;
  }
  BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
  return vf_Failure;
}

vfHRESULT vfRestoresColorLineWidth(const char *objectID) {
  try {
    BOOST_LOG_TRIVIAL(info)
        << LogUtil::GetLogContent(__func__, "objectID:", objectID);
    int objId = std::stoull(objectID);
    HRESULT hr = ApiUtils::ClearThickenAndHighlight({objId});
    return vf_Success;
  } catch (const _com_error &e) {
    LogComError(__func__, e);
    BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
    return vf_Failure;
  }
}

vfHRESULT GetFaceNormalOnPoint(gm::FacePtr const &pFace,
                               ATL::CComSafeArray<double> &ptData,
                               DVec3d &dirNormal) {
  long onRes;
  ATL::CComSafeArray<double> uv(2);
  double dis = DBL_MIN;
  ATL::CComSafeArray<double> closestPoint(3);

  double disTol = 1e-6;

  if (FAILED(pFace->IsPointOn(ptData.GetSafeArrayPtr(), &onRes,
                              uv.GetSafeArrayPtr(), &dis,
                              closestPoint.GetSafeArrayPtr()))) {
    BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
    return vf_Failure;
  }

  if (fabs(dis) > disTol) {
    // 点在面外
    BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
    return vf_Failure;
  }

  LPSAFEARRAY pParam = nullptr;
  LPSAFEARRAY pGuessParam = nullptr;
  LPSAFEARRAY pFlags = nullptr;
  LPSAFEARRAY pMaxDeviations = nullptr;
  if (FAILED(pFace->GetParamAtPoint(1, ptData.GetSafeArrayPtr(), &pGuessParam,
                                    &pMaxDeviations, &pParam, &pFlags))) {
    BOOST_LOG_TRIVIAL(info) << "get pt para failed";
    BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
    return vf_Failure;
  }
  DestroySafaArray(pGuessParam);
  DestroySafaArray(pMaxDeviations);
  DestroySafaArray(pFlags);
  ATL::CComSafeArray<double> param;
  param.Attach(pParam);

  // 取得曲面在此点上的法向
  ATL::CComSafeArray<double> normal(3);
  if (FAILED(pFace->GetNormal(1, param.GetSafeArrayPtr(),
                              normal.GetSafeArrayPtr()))) {
    BOOST_LOG_TRIVIAL(info) << "get normal failed";
    BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
    return vf_Failure;
  }

  // 获得0度方向
  dirNormal = DVec3d{normal[0], normal[1], normal[2]};
  return vf_Success;
}

vfHRESULT GetFaceOrFacesNormalOnPoint(IDispatchPtr const &pFaceOrFaces,
                                      ATL::CComSafeArray<double> &ptData,
                                      DVec3d &dirNormal) {
  try {
    geom_entity_type geomType = ApiUtils::GetGeomType(pFaceOrFaces);
    if (geom_entity_type::vf_faces == geomType) {
      gm::FacesPtr pFaces = pFaceOrFaces;
      // 点只在一个面上
      for (long i = 0; i < pFaces->GetCount(); i++) {
        gm::FacePtr pFace = pFaces->Item(i + 1);
        if (vf_Success == GetFaceNormalOnPoint(pFace, ptData, dirNormal)) {
          return vf_Success;
        }
      }
      return vf_Failure;
    } else if (geom_entity_type::vf_face == geomType) {
      gm::FacePtr pFace = pFaceOrFaces;
      return GetFaceNormalOnPoint(pFace, ptData, dirNormal);
    }
    return vf_Failure;
  } catch (_com_error const &e) {
    return vf_Failure;
  }
  return vf_Failure;
}

vfHRESULT vfCalcRosette(std::vector<DPoint3d> &segPts, int64_t faceID,
                        int64_t pointID, int64_t lineID, double startAngle,
                        double directionAngle, double segmentLength,
                        bool isRightHandDirection)

{
  segPts.clear();
  // 线长解释为MM
  segmentLength *= MM_TO_COORD;

  IDispatchPtr pFace = ApiUtils::FindEntityByID(faceID);
  gm::EdgePtr pDirctionCv = ApiUtils::FindEntityByID(lineID);
  gm::VertexPtr pBaseVtx = ApiUtils::FindEntityByID(pointID);
  if (nullptr == pFace || nullptr == pDirctionCv || nullptr == pBaseVtx) {
    BOOST_LOG_TRIVIAL(info) << "face dirCv vertex is null";
    BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
    return vf_Failure;
  }
  ATL::CComSafeArray<double> ptData(3);
  if (FAILED(pBaseVtx->GetPointData(ptData.GetSafeArrayPtr()))) {
    BOOST_LOG_TRIVIAL(info) << "getpt data failed";
    BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
    return vf_Failure;
  }

  DVec3d dirNormal;
  if (vf_Success != GetFaceOrFacesNormalOnPoint(pFace, ptData, dirNormal)) {
    BOOST_LOG_TRIVIAL(info) << "get normal failed";
    BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
    return vf_Failure;
  }

  // 此点在面上的参数
  LPSAFEARRAY pParam = nullptr;
  LPSAFEARRAY pGuessParam = nullptr;
  LPSAFEARRAY pFlags = nullptr;
  LPSAFEARRAY pMaxDeviations = nullptr;
  ATL::CComSafeArray<double> param;
  // 取得方向线在此点上的导数作为方向
  if (FAILED(pDirctionCv->GetParamAtPoint(1, ptData.GetSafeArrayPtr(),
                                          &pGuessParam, &pMaxDeviations,
                                          &pParam, &pFlags))) {

    BOOST_LOG_TRIVIAL(info) << "get deviation failed";
    BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
    return vf_Failure;
  }

  DestroySafaArray(pGuessParam);
  DestroySafaArray(pMaxDeviations);
  DestroySafaArray(pFlags);
  param.Attach(pParam);

  ATL::CComSafeArray<double> fstDevaition(3);
  ATL::CComSafeArray<double> scdDevaition(3);
  ATL::CComSafeArray<double> trdDevaition(3);
  if (FAILED(pDirctionCv->GetDerivatives(
          1, param.GetSafeArrayPtr(), fstDevaition.GetSafeArrayPtr(),
          scdDevaition.GetSafeArrayPtr(), trdDevaition.GetSafeArrayPtr()))) {
    BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
    return vf_Failure;
  }

  // 左手坐标系时
  if (!isRightHandDirection) {
    dirNormal = -1 * dirNormal;
  }

  DVec3d cvDir{fstDevaition[0], fstDevaition[1], fstDevaition[2]};
  bool cvIsZeroDir = (fabs(directionAngle - 0) < 1e-6);
  DVec3d zeroDir;
  if (cvIsZeroDir) {
    // 传入曲线是0度方向
    zeroDir = cvDir;
  } else {
    // 传入曲线是90度方向
    zeroDir = cvDir.cross(dirNormal);
  }
  zeroDir.normalize();

  bool startIsZero = (fabs(startAngle - 0) < 1e-6);
  int stAngle;
  if (startIsZero) {
    // 起始角是0度，显示0 45 90 135
    stAngle = 0;
  } else {
    // 起始角是-45度，显示 -45 0 45 90
    stAngle = -45;
  }

  // 遍历绘制四条线，如其中一条线是0度，记录终点和方向便于绘制箭头
  DPoint3d ptSt{ptData[0], ptData[1], ptData[2]};
  DPoint3d ptArrow;
  DPoint3d dirArrow;
  int angelZeroIdx = -1;
  for (int i = 0; i < 4; i++) {
    int angle = stAngle + i * 45;
    double angleInRadian = (angle * M_PI) / 180;
    Eigen::AngleAxisd rot(angleInRadian, dirNormal);
    DVec3d dir = rot.toRotationMatrix() * zeroDir;
    DPoint3d ptEnd = ptSt + dir * segmentLength;
    int64_t segId;
    if (angle == 0) {
      ptArrow = ptEnd;
      dirArrow = dir;
      angelZeroIdx = i;
    }
    segPts.insert(segPts.end(), {ptSt, ptEnd});
  }

  // 由于特定要求0度为线一，线二、线三组成箭头
  if (angelZeroIdx != 0) {
    std::swap(segPts[0], segPts[angelZeroIdx * 2]);
    std::swap(segPts[1], segPts[angelZeroIdx * 2 + 1]);
  }

  // 绘制箭头(左右各30度)
  Eigen::AngleAxisd rot30(M_PI / 6, dirNormal);
  DVec3d dirArrow30 = -1 * (rot30.toRotationMatrix() * dirArrow);
  DVec3d dirArrowNeg30 = -1 * (rot30.toRotationMatrix().inverse() * dirArrow);
  double arrowLen = 0.12 * segmentLength;
  segPts.insert(segPts.begin() + 2, {ptArrow, ptArrow + arrowLen * dirArrow30});
  segPts.insert(segPts.begin() + 2,
                {ptArrow, ptArrow + arrowLen * dirArrowNeg30});
  return vf_Success;
}

vfHRESULT CreatePattern(std::vector<int64_t> const &featureIdArr,
                        int64_t refPlaneId) {

  try {
    pt::PartDocumentPtr pPartDoc = SEAPP->GetActiveDocument();
    pt::PatternsPtr pPatterns = pPartDoc->GetModels()->Item(1l)->GetPatterns();
    ATL::CComSafeArray<IDispatch *> featureArr(featureIdArr.size());
    for (int i = 0; i < featureIdArr.size(); i++) {
      IDispatchPtr pDisp = ApiUtils::FindFeatureByID(featureIdArr[i]);
      featureArr.SetAt(i, pDisp);
    }

    pt::RefPlanePtr pRefPlane = ApiUtils::FindFeatureByID(refPlaneId);
    pt::ProfilePtr pProfile =
        pPartDoc->GetProfileSets()->Add()->GetProfiles()->Add(pRefPlane);

    fwp::RectangularPatterns2dPtr pPatterns2d =
        pProfile->GetRectangularPatterns2d();

    pPatterns2d->Add(0.01, 0.01, 0.1, 0.05, 0,
                     fwp::PatternOffsetTypeConstants::sePatternFixedOffset, 3,
                     3, 0.04, 0.04);
    pPatterns->Add(featureArr.GetCount(), featureArr.GetSafeArrayPtr(),
                   pProfile, pt::PatternTypeConstants::seFastPattern);

    return vf_Success;
  } catch (_com_error const &e) {
    BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
    return vf_Failure;
  }
  BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
  return vf_Failure;
}

vfHRESULT CalcRosetteSegments(int &ptSize, double *&pPts, int64_t faceID,
                              int64_t pointID, int64_t lineID,
                              double startAngle, double directionAngle,
                              double segmentLength, bool isRightHandDirection) {
  try {
    std::vector<DPoint3d> segPts;
    if (vf_Success != vfCalcRosette(segPts, faceID, pointID, lineID, startAngle,
                                    directionAngle, segmentLength,
                                    isRightHandDirection)) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }
    ptSize = segPts.size();
    pPts = new double[ptSize * 3];
    memcpy(pPts, segPts[0].data(), segPts.size() * 3 * sizeof(double));
    return vf_Success;
  } catch (_com_error const &e) {
    LogComError(__func__, e);
    BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
    return vf_Failure;
  }
  return vf_Success;
}

vfHRESULT vfCreateRosetteById(int64_t faceID, int64_t pointID, int64_t lineID,
                              double startAngle, double directionAngle,
                              double segmentLength, bool isRightHandDirection,
                              int64_t *RosetteID, int64_t *Line1,
                              int64_t *Line2, int64_t *Line3, int64_t *Line4,
                              int64_t *Line5, int64_t *Line6) {
  try {
    std::vector<DPoint3d> segPts;
    if (vf_Success != vfCalcRosette(segPts, faceID, pointID, lineID, startAngle,
                                    directionAngle, segmentLength,
                                    isRightHandDirection)) {
      BOOST_LOG_TRIVIAL(info) << "calc rosetter failed";
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }
    pt::PartDocumentPtr pPartDoc = SEAPP->GetActiveDocument();
    pt::Sketches3DPtr pSketches = pPartDoc->GetSketches3D();

    // 新建一个三维草图，起名为Rosette{%d}格式
    long sketchCnt = pSketches->GetCount();
    int maxRosetteIdx = 0;
    for (long i = 0; i < sketchCnt; i++) {
      pt::Sketch3DPtr pSketch = pSketches->Item(i + 1);
      _bstr_t name = pSketch->GetName();
      int rosetteIdx;
      if (sscanf_s(name, "Rosette%d", &rosetteIdx)) {
        maxRosetteIdx = (std::max)(rosetteIdx, maxRosetteIdx);
      }
    }

    _bstr_t sketchName;
    sketchName = std::format(L"Rosette{0}", maxRosetteIdx + 1).c_str();
    pt::Sketch3DPtr pSketch = pSketches->Add();
    pSketch->PutName(sketchName);

    // 一次创建六条线段
    pt::Lines3DPtr pLines3d = pSketch->GetLines3D();
    ATL::CComSafeArray<double> ptComData(segPts.size() * 3);
    memcpy((*ptComData.GetSafeArrayPtr())->pvData, segPts[0].data(),
           segPts.size() * 3 * sizeof(double));

    ATL::CComSafeArray<IDispatch *> lineArr(6);
    HRESULT hr = pLines3d->AddMultiple(6, ptComData.GetSafeArrayPtr(),
                                       lineArr.GetSafeArrayPtr());
    if (FAILED(hr) || lineArr.GetCount() != 6) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    // 分别获得6条线段ID及特征ID
    std::array<int64_t, 6> segIds;
    for (long i = 0; i < 6; i++) {
      IDispatch *pDisp = lineArr.GetAt(i);
      pt::Line3DPtr pLine = pDisp;
      gm::EdgePtr pEdge = ApiUtils::EntityToGeomtry(pLine);
      segIds[i] = ApiUtils::GetEntityID(pEdge);
      if (UINT64_MAX == segIds[i]) {
        BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
        return vf_Failure;
      }
    }
    *Line1 = segIds[0];
    *Line2 = segIds[1];
    *Line3 = segIds[2];
    *Line4 = segIds[3];
    *Line5 = segIds[4];
    *Line6 = segIds[5];
    // 特征ID返回为负数
    *RosetteID = ApiUtils::GetFeatureID(pSketch);

    return vf_Success;
  } catch (const _com_error &e) {
    LogComError(__func__, e);
    BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
    return vf_Failure;
  }
}

vfHRESULT vfRemoveTargetObject(const char *objectID) {
  try {
    BOOST_LOG_TRIVIAL(info)
        << LogUtil::GetLogContent(__func__, "objectID:", objectID);

    IDispatchPtr pEnt = ApiUtils::FindEntityByID(objectID);
    geom_entity_type geoType;
    if (vfGetEntityType(objectID, geoType)) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    switch (geoType) {
    case vf_face: {
      IDispatchPtr pParent = ApiUtils::GetParent(pEnt);
      if (nullptr == pParent) {
        BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
        return vf_Failure;
      }

      HRESULT hr = ApiUtils::InvokeDelete(pParent);
      if (FAILED(hr)) {
        BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
        return vf_Failure;
      }
      return vf_Success;
      break;
    }
    case vf_body: {
      IDispatchPtr pParent = ApiUtils::GetParent(pEnt);
      if (nullptr == pParent) {
        BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
        return vf_Failure;
      }

      pt::ModelPtr pModel = pParent;
      pt::ConstructionModelPtr pConstrModel = pParent;
      pt::FeaturesPtr pFeatArr = nullptr;
      // 删除体=删除模型=删除其下所有特征
      if (nullptr != pModel) {
        pFeatArr = pModel->GetFeatures();
      } else if (nullptr != pConstrModel) {
        pFeatArr = pConstrModel->GetFeatures();
      } else {
        BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
        return vf_Failure;
      }
      long featCnt = pFeatArr->GetCount();
      while (featCnt > 0) {
        IDispatchPtr pFeat = pFeatArr->Item(featCnt);
        HRESULT hr = ApiUtils::InvokeDelete(pFeat);
        if (FAILED(hr)) {
          BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
          return vf_Failure;
        }

        featCnt--;
      }
      return vf_Success;
      break;
    }
    case vf_curve: {
      int64_t edgeParentId = ApiUtils::GetEdgeSegmentID(pEnt);
      if (UINT64_MAX == edgeParentId) {
        BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
        return vf_Failure;
      }

      IDispatchPtr pParent = ApiUtils::FindFeatureByID(edgeParentId);
      if (NULL == pParent) {
        BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
        return vf_Failure;
      }
      HRESULT hr = ApiUtils::InvokeDelete(pParent);
      if (FAILED(hr)) {
        BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
        return vf_Failure;
      }
      return vf_Success;

      break;
    }
    case vf_vertex: {
      pt::PartDocumentPtr pPartDoc = SEAPP->GetActiveDocument();
      gm::VertexPtr pVtx = pEnt;
      _variant_t ptIdx;
      pt::Point3DPtr pPt = pPartDoc->GetVertexOwnerSketch3DPoint(pVtx, &ptIdx);
      if (nullptr == pPt) {
        BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
        return vf_Failure;
      }
      pt::Sketch3DPtr pSketch = pPt->GetParent();
      pt::Points3DPtr pPts = pSketch->GetPoints3D();
      HRESULT hr = pPts->DeletePointByIndex(ptIdx);
      if (FAILED(hr)) {
        BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
        return vf_Failure;
      }
      return vf_Success;
      break;
    }
    case vf_loop: {

      break;
    }
    case vf_curves: {
      // 线集
      // 如果第一个线的父对象为三维草图，则对每一子线进行删除
      // 否则认为线集为特征的结果，删除父特征即可
      gm::EdgesPtr pCurves = pEnt;
      gm::EdgePtr pFstEdge = pCurves->Item(1l);
      IDispatchPtr pParentDisp = pFstEdge->GetParent();
      int64_t edgeCreateId = ApiUtils::GetEdgeSegmentID(pFstEdge);
      IDispatchPtr pParent = ApiUtils::FindFeatureByID(edgeCreateId);
      if (nullptr == pParent) {
        BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
        return vf_Failure;
      }

      pt::Sketch3DPtr pSketch3d = pParent;
      if (nullptr != pSketch3d) {
        for (long i = 1; i <= pCurves->GetCount(); i++) {
          gm::EdgesPtr pEdge = pCurves->Item(i);
          int64_t edgeParentId = ApiUtils::GetEdgeSegmentID(pEnt);
          if (UINT64_MAX == edgeParentId) {
            BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
            break;
          }

          IDispatchPtr pParent = ApiUtils::FindFeatureByID(edgeParentId);
          if (NULL == pParent) {
            BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
            break;
          }
          HRESULT hr = ApiUtils::InvokeDelete(pParent);
          if (FAILED(hr)) {
            BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
            break;
          }
        }
      } else {
        int objId;
        ApiUtils::GetLocatedGraphicType(pParent, objId);
        long geomType = ApiUtils::GetGeomType(pParent);

        HRESULT hr = ApiUtils::InvokeDelete(pParent);
        if (FAILED(hr)) {
          BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
          break;
        }
      }

      break;
    }
    case vf_faces: {
      gm::FacesPtr pFaces = pEnt;
      gm::FacePtr pFstFace = pFaces->Item(1l);
      IDispatchPtr pParentFeature = pFstFace->GetParent();
      pt::PartDocumentPtr pPartDoc = SEAPP->GetActiveDocument();
      ApiUtils::DelFeatureRecursive(pPartDoc, pParentFeature);
      return vf_Success;
      break;
    }

    default:
      break;
    }

    // 调用Delete方法
    BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
    return vf_Failure;
  } catch (const _com_error &e) {
    LogComError(__func__, e);
    BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
    return vf_Failure;
  }
}

vfHRESULT vfChangeLineColorWidthInsertMap(const char *inputStringValue[]) {

  BOOST_LOG_TRIVIAL(info) << LogUtil::GetLogContent(
      __func__, "inputStringValue:", inputStringValue);
  if (nullptr == inputStringValue) {
    BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
    return vf_Failure;
  }
  try {
    std::vector<int64_t> topoIds;
    while (*inputStringValue) {
      std::string idStr = *inputStringValue;
      if (idStr.empty()) {
        inputStringValue++;
        continue;
      }
      int64_t topoId = std::stoull(idStr);
      topoIds.push_back(topoId);
      inputStringValue++;
    }
    if (topoIds.empty()) {
      return vf_Success;
    }

    HRESULT hr = ApiUtils::ThickenAndHighlight(topoIds);
    return (SUCCEEDED(hr) ? vf_Success : vf_Failure);
  } catch (const _com_error &e) {
    LogComError(__func__, e);
    BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
    return vf_Failure;
  }
  BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
  return vf_Failure;
}

vfHRESULT vfChangeLineColorWidthFromMap(const char *inputStringValue[]) {
  BOOST_LOG_TRIVIAL(info) << LogUtil::GetLogContent(
      __func__, "inputStringValue:", inputStringValue);
  if (nullptr == inputStringValue) {
    BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
    return vf_Failure;
  }
  try {
    std::vector<int64_t> topoIds = ApiUtils::CollectIds(inputStringValue);
    if (topoIds.empty()) {
      return vf_Success;
    }
    HRESULT hr = ApiUtils::ClearThickenAndHighlight(topoIds);
    if (FAILED(hr)) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }
    // 刷新下视图
    fw::WindowPtr pActiveWindow = SEAPP->GetActiveWindow();
    pActiveWindow->GetView()->Update();
    return vf_Success;
  } catch (const _com_error &e) {
    LogComError(__func__, e);
    BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
    return vf_Failure;
  }
  BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
  return vf_Failure;
}

vfHRESULT vfCreateRosette(const char *faceID, const char *pointID,
                          const char *lineID, double startAngle,
                          double directionAngle, double segmentLength,
                          const char *HandDirection, const char *&RosetteID,
                          const char *&Line1, const char *&Line2,
                          const char *&Line3, const char *&Line4,
                          const char *&Line5, const char *&Line6) {

  try {
    BOOST_LOG_TRIVIAL(info) << LogUtil::GetLogContent(
        __func__, "faceID:", faceID, "PointID:", pointID, "LineID:", lineID,
        "directionAngle:", directionAngle, "segmentLength:", segmentLength,
        "HandDirection:", HandDirection);
    int64_t idFace = std::stoull(faceID);
    int64_t idPoint = std::stoull(pointID);
    int64_t idLine = std::stoull(lineID);
    int64_t idLine1, idLine2, idLine3, idLine4, idLine5, idLine6;
    int64_t idRosette;
    std::string dirStr = HandDirection;
    boost::to_upper(dirStr);
    bool isRight = ("RIGHT" == dirStr);
    vfHRESULT status =
        vfCreateRosetteById(idFace, idPoint, idLine, startAngle, directionAngle,
                            segmentLength, isRight, &idRosette, &idLine1,
                            &idLine2, &idLine3, &idLine4, &idLine5, &idLine6);
    if (status != vf_Success) {
      return status;
    }
    std::string rosetteIDStr = std::to_string(idRosette);
    std::string line1Str = std::to_string(idLine1);
    std::string line2Str = std::to_string(idLine2);
    std::string line3Str = std::to_string(idLine3);
    std::string line4Str = std::to_string(idLine4);
    std::string line5Str = std::to_string(idLine5);
    std::string line6Str = std::to_string(idLine6);

    RosetteID = _strdup(rosetteIDStr.c_str());
    Line1 = _strdup(line1Str.c_str());
    Line2 = _strdup(line2Str.c_str());
    Line3 = _strdup(line3Str.c_str());
    Line4 = _strdup(line4Str.c_str());
    Line5 = _strdup(line5Str.c_str());
    Line6 = _strdup(line6Str.c_str());
    return vf_Success;
  } catch (const _com_error &e) {
    LogComError(__func__, e);
    BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
    return vf_Failure;
  }
  BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
  return vf_Failure;
}

vfHRESULT vfSelectionToID(const wchar_t *filterType, char *intValue) {
  try {
    BOOST_LOG_TRIVIAL(info)
        << LogUtil::GetLogContent(__func__, "filterType:", filterType);
    // 解析筛选器
    std::string filterStr = UnicodeToUtf8(filterType);
    std::vector<std::string> filterList;
    boost::split(filterList, filterStr, boost::is_any_of(","),
                 boost::token_compress_on);
    int geoType = 0;
    if (filterList.empty()) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    std::wstring promptStr = L"";
    for (std::string str : filterList) {
      boost::to_upper(str);
      if ("POINT" == str) {
        geoType |= vf_vertex;
        promptStr += L"点,";
      } else if ("CURVE" == str) {
        geoType |= vf_curve;
        promptStr += L"线,";
      } else if ("SURFACE" == str) {
        geoType |= vf_face;
        promptStr += L"面,";
      } else {
        // 暂时只支持此三种
        BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
        return vf_Failure;
      }
    }
    promptStr.pop_back();
    promptStr = L"请选择" + promptStr;

    int64_t tagID = 0;
    SelReturnData rtData;
    vfHRESULT status =
        SelectByInteractive(rtData, geoType, true, promptStr.data(), false);
    if (status != vf_Success || rtData.ElementIds.empty()) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    tagID = rtData.ElementIds[0];
    std::string tagIDStr = std::to_string(tagID);
    strcpy_s(intValue, tagIDStr.size() + 1, tagIDStr.c_str());

    return vf_Success;
  } catch (const _com_error &e) {
    LogComError(__func__, e);
    BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
    return vf_Failure;
  }

  BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
  return vf_Failure;
}

vfHRESULT vfCESHI(const char *inputStringValue[], int32_t *lineWidth,
                  int32_t *colorR, int32_t *colorG, int32_t *colorB) {

  try {
    BOOST_LOG_TRIVIAL(info) << LogUtil::GetLogContent(
        __func__, "inputStringValue:", inputStringValue,
        "lineWidth:", *lineWidth, "colorR:", *colorR, "colorG:", *colorG,
        "colorB:", *colorB);
  } catch (const _com_error &e) {
    LogComError(__func__, e);
    BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
    return vf_Failure;
  }
  return vf_Success;
}

vfHRESULT vfParameterInput(const char *userInput) {
  try {
    BOOST_LOG_TRIVIAL(info)
        << LogUtil::GetLogContent(__func__, "userInput:", userInput);

  } catch (const _com_error &e) {
    LogComError(__func__, e);
    BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
    return vf_Failure;
  }
  return vf_Success;
}

vfHRESULT vfSelectionToIDMore(const wchar_t *filterType,
                              const char **&outputID) {
  try {
    BOOST_LOG_TRIVIAL(info) << __func__;
    std::string strFilterType = UnicodeToUtf8(filterType);
    std::vector<std::string> filterStrArr;
    boost::split(filterStrArr, strFilterType, boost::is_any_of(","),
                 boost::token_compress_off);
    if (filterStrArr.empty()) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    int filterFlag = 0;
    for (auto const &str : filterStrArr) {
      if (str == "Point") {
        filterFlag |= (int)geom_entity_type::vf_vertex;
      } else if (str == "Curve") {
        filterFlag |= (int)geom_entity_type::vf_curve;
      } else if (str == "Surface") {
        filterFlag |= (int)geom_entity_type::vf_face;
      }
    }

    std::wstring promptStr = L"Please Slect ";
    promptStr += filterType;
    std::vector<int64_t> selObjIds;
    vfHRESULT status = vfSelectMultiyInteractive(
        filterFlag, selObjIds, const_cast<wchar_t *>(promptStr.c_str()), false);

    if (status != vf_Success || selObjIds.empty()) {
      return status;
    }

    outputID = new const char *[selObjIds.size() + 1] { nullptr };
    for (int i = 0; i < selObjIds.size(); i++) {
      std::string idStr = std::to_string(selObjIds[i]);
      outputID[i] = _strdup(idStr.c_str());
    }

    return vf_Success;
  } catch (const _com_error &e) {
    LogComError(__func__, e);
    BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
    return vf_Failure;
  }
  return vf_Success;
}

vfHRESULT vfCurveLength(const char *CurveID, double &Length) {
  try {
    BOOST_LOG_TRIVIAL(info)
        << LogUtil::GetLogContent(__func__, "CurveID:", CurveID);
    int64_t idCurve = std::stoull(CurveID);
    IDispatchPtr pCurve = ApiUtils::FindEntityByID(idCurve);
    if (nullptr == pCurve) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    geom_entity_type geomType = ApiUtils::GetGeomType(pCurve);
    if (geomType != geom_entity_type::vf_curve &&
        geom_entity_type::vf_curves != geomType) {
      // 传入应该是线或线组
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    if (geomType == geom_entity_type::vf_curve) {
      // 线时
      gm::EdgePtr pEdge = pCurve;
      if (!ApiUtils::GetEdgeLength(pEdge, Length)) {
        BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
        return vf_Failure;
      }
      Length *= COORD_TO_MM;

    } else if (geomType == geom_entity_type::vf_curves) {
      // 线组
      gm::EdgesPtr pEdges = pCurve;
      if (nullptr == pEdges) {
        BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
        return vf_Failure;
      }
      Length = 0.0;
      for (long i = 0; i < pEdges->GetCount(); i++) {
        gm::EdgePtr pEdge = pEdges->Item(i + 1);
        double tempLength = 0.0;
        if (!ApiUtils::GetEdgeLength(pEdge, tempLength)) {
          BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
          return vf_Failure;
        }
        Length += tempLength;
      }
      Length *= COORD_TO_MM;
    }
    BOOST_LOG_TRIVIAL(info)
        << LogUtil::GetLogContent(__func__, "Length:", Length);
    return vf_Success;
  } catch (const _com_error &e) {
    LogComError(__func__, e);
    BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
    return vf_Failure;
  }
}

vfHRESULT vfCreatePointByProjection(const char *PointID1, const char *SurfaceID,
                                    const char *&PointID2) {
  try {
    BOOST_LOG_TRIVIAL(info) << LogUtil::GetLogContent(
        __func__, "PointID1:", PointID1, "SurfaceID:", SurfaceID);
    int64_t idPoint = std::stoull(PointID1);
    int64_t surfaceID = std::stoull(SurfaceID);

    pt::PartDocumentPtr pPartDoc = SEAPP->GetActiveDocument();
    // 数据转换
    gm::VertexPtr pVertex = ApiUtils::FindEntityByID(idPoint);
    gm::FacePtr pFace = ApiUtils::FindEntityByID(surfaceID);
    if (nullptr == pVertex || nullptr == pFace || nullptr == pPartDoc) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }
    pt::RefPlanePtr pRefplane = pPartDoc->GetRefPlanes()->AddParallelByDistance(
        pFace, 0.0, pt::ReferenceElementConstants::igNormalSide);

    // 添加一个投影曲线
    pt::ProjectCurvesPtr pProjectCurves =
        pPartDoc->Constructions->ProjectCurves;
    pt::ProjectCurvePtr pProjectCurve = pProjectCurves->Add(
        pVertex, pFace, pRefplane, pt::FeaturePropertyConstants::igBoth,
        pt::FeaturePropertyConstants::igProjectOptionProject);
    // 返回投影曲线拓朴ID

    // edgeCnt和faceCnt都是0
    // gm::EdgesPtr pEdges = pProjectCurve->Edges[gm::igQueryAll];
    // int edgeCnt = pEdges->GetCount();
    // gm::FacesPtr pfaces = pProjectCurve->GetFaces(gm::igQueryAll);
    // int faceCnt = pfaces->GetCount();
    double xMin, yMin, zMin, xMax, yMax, zMax;
    pProjectCurve->Range(&xMin, &yMin, &zMin, &xMax, &yMax, &zMax);
    pProjectCurve->Delete();

    if (vf_Success != vfCreatePoint(xMax, yMax, zMax, PointID2)) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    return vf_Success;
  } catch (const _com_error &e) {
    LogComError(__func__, e);
    BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
    return vf_Failure;
  }

  return vf_Success;
}

vfHRESULT vfCreateSplitOrTrim(const char *CurveID1, const char *CurveID2,
                              const char *CutType, int iOrientation1,
                              int iOrientation2) {
  try {
    BOOST_LOG_TRIVIAL(info) << LogUtil::GetLogContent(
        __func__, "CurveID1:", CurveID1, "CurveID2:", CurveID2,
        "CutType:", CutType, "iOrientation1:", iOrientation1,
        "iOrientation2:", iOrientation2);

    int64_t objIdToTrim = std::stoull(CurveID1);
    int64_t objTrimingID = std::stoull(CurveID2);
    std::string cutTypeStr = CutType;
    if (cutTypeStr == "Split") {
      // 分割情况
      // 计算交点，如有交点，根据不同的情况，保留不同的方向

      // 计算交点
      double x, y, z;
      int64_t ptIntersectId;
      vfHRESULT status = vfCurveCurveIntersection_old(
          objIdToTrim, objTrimingID, x, y, z, false, ptIntersectId);
      if (status != vf_Success) {
        BOOST_LOG_TRIVIAL(error) << "计算两曲线交点有误";
        return status;
      }
      gm::EdgePtr pCurveToTrim = ApiUtils::FindEntityByID(objIdToTrim);
      if (nullptr == pCurveToTrim) {
        return status;
      }

      // 拿到三维草图待分割的曲线一
      IDispatchPtr pOriginCvToTrim =
          ApiUtils::Sketch3dTopoToObject(pCurveToTrim);

      IDispatchPtr pOriginCvTrimimg = ApiUtils::Sketch3dTopoToObject(
          ApiUtils::FindEntityByID(objTrimingID));
      if (nullptr == pOriginCvToTrim || nullptr == pOriginCvTrimimg) {
        BOOST_LOG_TRIVIAL(error) << "由拓扑边获得三维草图实体有误";
        BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
        return vf_Failure;
      }

      pt::Sketch3DPtr pSketch3d = ApiUtils::GetParent(pOriginCvToTrim);
      if (nullptr == pOriginCvToTrim) {
        BOOST_LOG_TRIVIAL(error) << "由三维草图曲线实体未找到所在的三维草图";
        BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
        return vf_Failure;
      }

      IDispatchPtr pCvNegDirTrimed =
          pSketch3d->Split3DSegment(pOriginCvToTrim, x, y, z);

      IDispatchPtr pCvNegDirTrimming =
          pSketch3d->Split3DSegment(pOriginCvTrimimg, x, y, z);
      if (nullptr == pCvNegDirTrimed || nullptr == pCvNegDirTrimming) {
        BOOST_LOG_TRIVIAL(error) << "在交点处分割两原曲线有误";
      }

      // 保留正方向时，删除负方向线; 保留负方向时，删除正方向线
      IDispatchPtr pCvToDeleteIn1 =
          (iOrientation1 == 1) ? pCvNegDirTrimed : pOriginCvToTrim;

      IDispatchPtr pCvToDeleteIn2 =
          (iOrientation2 == 1) ? pCvNegDirTrimming : pOriginCvTrimimg;
      HRESULT hr = ApiUtils::InvokeDelete(pCvToDeleteIn1);
      hr = ApiUtils::InvokeDelete(pCvToDeleteIn2);

    } else if (cutTypeStr == "Trim") {
      // 裁剪情况
      const char *idTrimRes = nullptr;
      vfCurveTrim(CurveID1, CurveID2, idTrimRes);
      if (nullptr != idTrimRes) {
        free((void *)idTrimRes);
      }
    }

  } catch (const _com_error &e) {
    LogComError(__func__, e);
    BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
    return vf_Failure;
  }
  BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
  return vf_Failure;
}

vfHRESULT vfGetUnitTangentVectorAtPoint(const char *CurveID,
                                        const char *PointID, int bReverse,
                                        double &x, double &y, double &z) {
  try {
    BOOST_LOG_TRIVIAL(info) << LogUtil::GetLogContent(
        __func__, "CurveID:", CurveID, "PointID:", PointID,
        "是否翻转方向:", bReverse);

    gm::EdgePtr pCurve = ApiUtils::FindEntityByID(std::stoull(CurveID));
    gm::VertexPtr pVertex = ApiUtils::FindEntityByID(std::stoull(PointID));
    if (nullptr == pCurve || nullptr == pVertex) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }
    ATL::CComSafeArray<double> ptData(3);
    pVertex->GetPointData(ptData.GetSafeArrayPtr());

    // 此点在线上的参数
    LPSAFEARRAY pParam = nullptr;
    LPSAFEARRAY pGuessParam = nullptr;
    LPSAFEARRAY pFlags = nullptr;
    LPSAFEARRAY pMaxDeviations = nullptr;
    if (FAILED(pCurve->GetParamAtPoint(1, ptData.GetSafeArrayPtr(),
                                       &pGuessParam, &pMaxDeviations, &pParam,
                                       &pFlags))) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }
    DestroySafaArray(pGuessParam);
    DestroySafaArray(pMaxDeviations);
    DestroySafaArray(pFlags);
    ATL::CComSafeArray<double> param;
    param.Attach(pParam);

    ATL::CComSafeArray<double> fstDerivative(3), secDerivative(3),
        trdDerivative(3);
    if (FAILED(pCurve->GetDerivatives(1, param.GetSafeArrayPtr(),
                                      fstDerivative.GetSafeArrayPtr(),
                                      secDerivative.GetSafeArrayPtr(),
                                      trdDerivative.GetSafeArrayPtr()))) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    DVec3d derivative{fstDerivative[0], fstDerivative[1], fstDerivative[2]};
    derivative.normalize();

    x = derivative[0];
    y = derivative[1];
    z = derivative[2];

    if (bReverse == 1) {
      x = -x;
      y = -y;
      z = -z;
    }

    BOOST_LOG_TRIVIAL(info)
        << LogUtil::GetLogContent(__func__, "x:", x, "y:", y, "z:", z);

    // DPoint3d ptFst{ptData[0], ptData[1], ptData[2]};
    // DPoint3d vec{x, y, z};
    // DPoint3d ptScd = ptFst + vec * 10;
    // int64_t segId;
    // CreateSegment(segId, ptFst, ptScd);

    return vf_Success;
  } catch (const _com_error &e) {
    LogComError(__func__, e);
    BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
    return vf_Failure;
  }
}

vfHRESULT vfGetUnitNormalVectorAtPoint2(const char *SurfaceID, double CoodX,
                                        double CoodY, double CoodZ,
                                        bool bReverse, double &x, double &y,
                                        double &z) {
  try {
    BOOST_LOG_TRIVIAL(info) << LogUtil::GetLogContent(
        __func__, "SurfaceID:", SurfaceID, "CoodX:", CoodX, "CoodY:", CoodY,
        "CoordZ:", CoodZ);
    if (SurfaceID == nullptr || SurfaceID[0] == '\0') {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    IDispatchPtr pFace = ApiUtils::FindEntityByID(std::stoull(SurfaceID));
    pt::PartDocumentPtr pDoc = SEAPP->GetActiveDocument();
    if (nullptr == pFace || nullptr == pDoc) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    ATL::CComSafeArray<double> ptData = ApiUtils::ToComSafeArray(
        {CoodX * MM_TO_COORD, CoodY * MM_TO_COORD, CoodZ * MM_TO_COORD});

    ATL::CComSafeArray<double> unitNormal(3);
    pDoc->GetNormalAtPoint(pFace.GetInterfacePtr(), ptData.GetSafeArrayPtr(),
                           unitNormal.GetSafeArrayPtr());
    x = unitNormal[0];
    y = unitNormal[1];
    z = unitNormal[2];
    if (bReverse == 1) {
      x = -x;
      y = -y;
      z = -z;
    }

    return vf_Success;
  } catch (_com_error const &e) {
    LogComError(__func__, e);
    return vf_Failure;
  }
}

vfHRESULT vfGetUnitNormalVectorAtPoint(const char *SurfaceID,
                                       const char *PointID, bool bReverse,
                                       double &x, double &y, double &z) {
  try {
    BOOST_LOG_TRIVIAL(info) << LogUtil::GetLogContent(
        __func__, "SurfaceID:", SurfaceID, "PointID:", PointID,
        "是否翻转方向:", bReverse);
    if (SurfaceID == nullptr || PointID == nullptr || SurfaceID[0] == '\0' ||
        PointID[0] == '\0') {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    pt::PartDocumentPtr pDoc = SEAPP->GetActiveDocument();
    IDispatchPtr pFace = ApiUtils::FindEntityByID(std::stoull(SurfaceID));
    gm::VertexPtr pVertex = ApiUtils::FindEntityByID(std::stoull(PointID));
    if (nullptr == pFace || nullptr == pVertex || nullptr == pDoc) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    ATL::CComSafeArray<double> ptData(3);
    pVertex->GetPointData(ptData.GetSafeArrayPtr());

    ATL::CComSafeArray<double> unitNormal(3);
    pDoc->GetNormalAtPoint(pFace.GetInterfacePtr(), ptData.GetSafeArrayPtr(),
                           unitNormal.GetSafeArrayPtr());
    x = unitNormal[0];
    y = unitNormal[1];
    z = unitNormal[2];
    if (bReverse) {
      x = -x;
      y = -y;
      z = -z;
    }
    return vf_Success;
  } catch (const _com_error &e) {
    LogComError(__func__, e);
    BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
    return vf_Failure;
  }
}

vfHRESULT vfGetPointByGeodesicDis(const char *surfaceID, double x, double y,
                                  double z, double dirx, double diry,
                                  double dirz, double geodesicDis,
                                  double &outputx, double &outputy,
                                  double &outputz, int &result) {
  try {
    BOOST_LOG_TRIVIAL(info) << LogUtil::GetLogContent(
        __func__, "surfaceID:", surfaceID, "x:", x, "y:", y, "z:", z,
        "dirx:", dirx, "diry:", diry, "dirz:", dirz,
        "geodesicDis:", geodesicDis);

    // 首先赋值为非法值
    x *= MM_TO_COORD;
    y *= MM_TO_COORD;
    z *= MM_TO_COORD;

    outputx = outputy = outputz = DBL_MAX;

    geodesicDis *= MM_TO_COORD;
    std::string idSurfStr = surfaceID;
    int64_t idFace = std::stoull(idSurfStr);
    IDispatchPtr pFace = ApiUtils::FindEntityByID(idFace);
    if (pFace == nullptr) {
      BOOST_LOG_TRIVIAL(info) << "face is nullptr";
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    pt::PartDocumentPtr pPartDoc = SEAPP->GetActiveDocument();
    if (nullptr == pPartDoc) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    ATL::CComSafeArray<double> ptOrg = ApiUtils::ToComSafeArray({x, y, z});
    ATL::CComSafeArray<double> dir =
        ApiUtils::ToComSafeArray({dirx, diry, dirz});
    ATL::CComSafeArray<double> ptRes(3);
    if (FAILED(pPartDoc->CalcGeodesicDisPoint(
            pFace, ptOrg.GetSafeArrayPtr(), dir.GetSafeArrayPtr(), geodesicDis,
            ptRes.GetSafeArrayPtr()))) {
      BOOST_LOG_TRIVIAL(info) << "CalcGeodesicDisPoint failed";
      result = 0;
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }
    outputx = ptRes[0] * COORD_TO_MM;
    outputy = ptRes[1] * COORD_TO_MM;
    outputz = ptRes[2] * COORD_TO_MM;
    // result = 1;
    BOOST_LOG_TRIVIAL(info)
        << "resPt is " << outputx << "," << outputy << "," << outputz;

    int64_t ptId;
    // vfCreatePoint(ptRes[0], ptRes[1], ptRes[2], ptId);

    // int64_t segId;
    // DVec3d dirVec{dirx, diry, dirz};
    // dirVec.normalize();
    // dirVec *= 0.02;
    // TGCreateLineStartDirection(x, y, z, dirVec[0], dirVec[1], dirVec[2],
    // segId);

    return vf_Success;
  } catch (const _com_error &e) {
    LogComError(__func__, e);
    BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
    return vf_Failure;
  }
}

vfHRESULT vfCreatPointByGeodesicDis2(const char *surfaceID,
                                     const char *pointID1, double x, double y,
                                     double z, double geodesicDis,
                                     double &outputx, double &outputy,
                                     double &outputz) {
  try {
    // 首先赋值为非法值
    outputx = outputy = outputz = DBL_MAX;

    BOOST_LOG_TRIVIAL(info) << LogUtil::GetLogContent(
        __func__, "surfaceID:", surfaceID, "pointID1:", pointID1, "x:", x,
        "y:", y, "z:", z, "geodesicDis:", geodesicDis);
    geodesicDis *= MM_TO_COORD;
    x *= MM_TO_COORD;
    y *= MM_TO_COORD;
    z *= MM_TO_COORD;

    std::string idSurfStr = surfaceID;
    std::string idVertexStr = pointID1;
    int64_t idFace = std::stoull(idSurfStr);
    int64_t idVtx = std::stoull(idVertexStr);
    IDispatchPtr pFace = ApiUtils::FindEntityByID(idFace);
    gm::VertexPtr pVtx = ApiUtils::FindEntityByID(idVtx);
    if (pFace == nullptr || pVtx == nullptr) {
      BOOST_LOG_TRIVIAL(info) << "face or vtx is nullptr";
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    pt::PartDocumentPtr pPartDoc = SEAPP->GetActiveDocument();
    if (nullptr == pPartDoc) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    ATL::CComSafeArray<double> ptOrg = ApiUtils::ToComSafeArray({x, y, z});
    ATL::CComSafeArray<double> dir = ApiUtils::ToComSafeArray({x, y, z});
    ATL::CComSafeArray<double> ptData(3);
    pVtx->GetPointData(ptData.GetSafeArrayPtr());
    ATL::CComSafeArray<double> resPt(3);

    if (FAILED(pPartDoc->CalcGeodesicDisPoint(
            pFace, ptData.GetSafeArrayPtr(), dir.GetSafeArrayPtr(), geodesicDis,
            resPt.GetSafeArrayPtr()))) {
      BOOST_LOG_TRIVIAL(error) << "CalcGeodesicDisPoint Failed";
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    outputx = resPt[0] * COORD_TO_MM;
    outputy = resPt[1] * COORD_TO_MM;
    outputz = resPt[2] * COORD_TO_MM;

    int64_t ptId;
    //  vfCreatePoint(resPt[0], resPt[1], resPt[2], ptId);

    return vf_Success;
  } catch (const _com_error &e) {
    LogComError(__func__, e);
    BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
    return vf_Failure;
  }
}

vfHRESULT vfGetCoordOfPoint(const char *PointID, double &x, double &y,
                            double &z) {
  try {
    BOOST_LOG_TRIVIAL(info)
        << LogUtil::GetLogContent(__func__, "PointID:", PointID);
    gm::VertexPtr pVertex = ApiUtils::FindEntityByID(std::stoull(PointID));
    if (nullptr == pVertex) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }
    ATL::CComSafeArray<double> ptData(3);
    pVertex->GetPointData(ptData.GetSafeArrayPtr());
    x = ptData[0] * COORD_TO_MM;
    y = ptData[1] * COORD_TO_MM;
    z = ptData[2] * COORD_TO_MM;
    BOOST_LOG_TRIVIAL(info) << LogUtil::GetLogContent(
        __func__, "PointID:", PointID, "x:", x, "y:", y, "z:", z);
    return vf_Success;
  } catch (const _com_error &e) {
    LogComError(__func__, e);
    BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
    return vf_Failure;
  }
}

vfHRESULT vfAddCommentOnPoint(const char *SurfaceID, double x, double y,
                              double z, BSTR Comment, int R, int G, int B,
                              BSTR Font, double Size, const char *&CommentID) {

  try {

    BOOST_LOG_TRIVIAL(info) << LogUtil::GetLogContent(
        __func__, "SurfaceID:", SurfaceID, "Comment:", Comment);
    pt::PartDocumentPtr pPartDoc = SEAPP->GetActiveDocument();
    if (nullptr == pPartDoc) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    // 找到点所在的拓扑面
    long bOn;
    ATL::CComSafeArray<double> uv(2);
    ATL::CComSafeArray<double> closePt(3);
    double closestDis;
    IDispatchPtr pFaceOrFaces = ApiUtils::FindEntityByID(SurfaceID);
    gm::FacesPtr pFaces = pFaceOrFaces;
    gm::FacePtr pDestFace = nullptr;
    ATL::CComSafeArray<double> pointData = ApiUtils::ToComSafeArray(
        {x * MM_TO_COORD, y * MM_TO_COORD, z * MM_TO_COORD});
    if (nullptr != pFaces) {
      for (long i = 0; i < pFaces->GetCount(); i++) {
        gm::FacePtr pFace = pFaces->Item(i + 1);
        pFace->IsPointOn(pointData.GetSafeArrayPtr(), &bOn,
                         uv.GetSafeArrayPtr(), &closestDis,
                         closePt.GetSafeArrayPtr());
        if (bOn) {
          // 如在任一面内或任一面边界内,
          pDestFace = pFace;
          break;
        }
      }
    } else {
      // 单个面时
      pDestFace = pFaceOrFaces;
    }

    LPSAFEARRAY pParam = nullptr;
    LPSAFEARRAY pGuessParam = nullptr;
    LPSAFEARRAY pFlags = nullptr;
    LPSAFEARRAY pMaxDeviations = nullptr;
    if (FAILED(pDestFace->GetParamAtPoint(1, pointData.GetSafeArrayPtr(),
                                          &pGuessParam, &pMaxDeviations,
                                          &pParam, &pFlags))) {
      BOOST_LOG_TRIVIAL(info) << "get pt para failed";
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }
    DestroySafaArray(pGuessParam);
    DestroySafaArray(pMaxDeviations);
    DestroySafaArray(pFlags);
    ATL::CComSafeArray<double> param;
    param.Attach(pParam);

    // 取得曲面在此点上的法向
    ATL::CComSafeArray<double> normal(3);
    if (FAILED(pDestFace->GetNormal(1, param.GetSafeArrayPtr(),
                                    normal.GetSafeArrayPtr()))) {
      BOOST_LOG_TRIVIAL(info) << "get normal failed";
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    LPSAFEARRAY pUTangent = nullptr;
    LPSAFEARRAY pVTangent = nullptr;
    LPSAFEARRAY puuTangent = nullptr;
    LPSAFEARRAY puvTangent = nullptr;
    LPSAFEARRAY pvvTangent = nullptr;
    LPSAFEARRAY pvvvTangent = nullptr;
    LPSAFEARRAY puuuTangent = nullptr;
    pDestFace->GetDerivatives(1, param.GetSafeArrayPtr(), &pUTangent,
                              &pVTangent, &puuTangent, &puvTangent, &pvvTangent,
                              &puuuTangent, &pvvvTangent);
    // pDestFace->GetTangents(1, param.GetSafeArrayPtr(), &pUTangent,
    // &pVTangent);
    ATL::CComSafeArray<double> uTangent, vTangent;
    uTangent.Attach(pUTangent);
    vTangent.Attach(pVTangent);
    SafeArrayDestroy(puuTangent);
    SafeArrayDestroy(puvTangent);
    SafeArrayDestroy(pvvTangent);
    SafeArrayDestroy(puuuTangent);
    SafeArrayDestroy(pvvvTangent);

    // clang-format off
    ATL::CComSafeArray<double> matrix = ApiUtils::ToComSafeArray({
		uTangent[0], uTangent[1], uTangent[2], 0.0,
		vTangent[0], vTangent[1], vTangent[2], 0.0,
		normal[0], normal[1], normal[2], 0.0,
		pointData[0], pointData[1], pointData[2], 1.0
		});

    // clang-format on
    pt::CoordinateSystemPtr pCoordSys =
        pPartDoc->GetCoordinateSystems()->AddByMatrix(matrix.GetSafeArrayPtr());
    gm::FacePtr pFace = pCoordSys->GetPlane(pt::seCoordSysXYPlane);
    gm::EdgePtr pPivotEdge = pCoordSys->GetAxis(pt::seCoordSysXAxis);
    pt::RefPlanePtr pRefPlane = pPartDoc->GetRefPlanes()->AddParallelByDistance(
        pFace, 0, pt::igNormalSide, pPivotEdge.GetInterfacePtr(),
        pt::igPivotStart, VARIANT_FALSE);

    // 创建相切参考面
    // auto pRefPlanes = pPartDoc->GetRefPlanes();
    // pt::RefPlanePtr pParentPlaen = pPartDoc->GetRefPlanes()->Item(1l);
    // pRefPlanes->AddTangentToCurvedSurfaceAtKeyPoint(
    //     pDestFace.GetInterfacePtr(), pParentPlaen.GetInterfacePtr(), )
    //  pt::RefPlanePtr pRefPlane = pPartDoc->GetRefPlanes()->Item(1l);
    fwp::PMIPtr pmi = pPartDoc->GetPMI();
    fwp::TextBoxesPtr pBoxes = pmi->TextBoxes;
    fwp::TextBoxPtr pBox =
        pBoxes->AddPMITextBox(pRefPlane.GetInterfacePtr(), x * MM_TO_COORD,
                              y * MM_TO_COORD, z * MM_TO_COORD);
    pBox->PutText(Comment);
    pBox->PutWidth(0.01);
    pBox->PutHeight(0.01);
    long color = RGB(R, G, B);
    fwp::TextEditPtr pEdit = pBox->GetEdit();
    pEdit->PutColor(color);

    // 字体暂时不动
    // pEdit->PutFont(Font);
    pEdit->PutTextSize(Size * MM_TO_COORD);

    // fwp::PMIPtr pmi = pPartDoc->GetPMI();
    // fwp::BalloonsPtr balloons = pmi->Balloons;
    // auto refPlanes = pPartDoc->RefPlanes;
    // auto selectPlane = refPlanes->Item(1);

    // auto bnInitData = balloons->AnnotInitData;
    // bnInitData->ClearTerminatorElement();
    // bnInitData->SetPlane(selectPlane);
    // bnInitData->SetTerminatorElementEx(pDestFace, true, 0.5, y, z);
    // auto ballon = balloons->AddBalloon(bnInitData);
    // ballon->Callout = false;
    // ballon->Leader = false;
    // ballon->BreakLine = false;
    // ballon->PutCalloutTextFontSize(Size * MM_TO_COORD);
    // ballon->PMIName = L"VFA_ballon";
    // ballon->posi
    // // 设置PMI的内容
    // ballon->BalloonText = Comment;
  } catch (_com_error const &e) {
    LogComError(__func__, e);
    BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
    return vf_Failure;
  }

  return vf_Success;
}

vfHRESULT vfDeleteCommentOnPoint(const char *PointID) {
  try {
    BOOST_LOG_TRIVIAL(info)
        << LogUtil::GetLogContent(__func__, "PointID:", PointID);
    pt::PartDocumentPtr partDocument = SEAPP->GetActiveDocument();
    if (partDocument == nullptr) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    int64_t idPoint = std::stoull(PointID);
    gm::VertexPtr findVertex = ApiUtils::FindEntityByID(idPoint);
    if (nullptr == findVertex) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    using namespace fwp;
    using namespace gm;
    PMIPtr pmi = partDocument->PMI;
    auto balloons = (BalloonsPtr)pmi->Balloons;
    for (auto i = 1; i <= balloons->Count; i++) {
      BalloonPtr balloon = balloons->Item(i);
      _bstr_t ballonName = balloon->GetPMIName();
      CString nameCstr = ballonName.operator const wchar_t *();
      if (nameCstr.Find(L"VFA_") != -1) {

        double x, y, z;
        IDispatchPtr pTerminator = nullptr;
        VARIANT_BOOL bKeyPoint;
        if (FAILED(
                balloon->GetTerminator(&pTerminator, &x, &y, &z, &bKeyPoint))) {
          continue;
        }
        if (pTerminator == findVertex) {
          balloon->Delete();
          break;
        }
      }
    }

  } catch (_com_error const &e) {
    LogComError(__func__, e);
    BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
    return vf_Failure;
  }
  return vf_Success;
}

std::map<int, fw::HighlightSetPtr> hiliSetsClrMap;
vfHRESULT vfChangeSurfaceColorInsertMap(const char *Surfaces[], int *R, int *G,
                                        int *B) {
  try {
    //  收集得曲面集
    BOOST_LOG_TRIVIAL(info)
        << LogUtil::GetLogContent(__func__, "Surfaces:", Surfaces);
    std::vector<std::int64_t> faceIds = ApiUtils::CollectIds(Surfaces);
    if (faceIds.empty()) {
      return vf_Success;
    }
    std::vector<int> clrRVals(faceIds.size());
    std::vector<int> clrGVals(faceIds.size());
    std::vector<int> clrBVals(faceIds.size());
    for (int i = 0; i < faceIds.size(); i++) {
      clrRVals[i] = R[i];
      clrGVals[i] = G[i];
      clrBVals[i] = B[i];
    }

    // 按颜色分类
    std::map<int, std::vector<int64_t>> clrFacesMap;
    for (int i = 0; i < faceIds.size(); i++) {
      int clr = RGB(clrRVals[i], clrGVals[i], clrBVals[i]);
      clrFacesMap[clr].push_back(faceIds[i]);
    }

    // 有多少种不同颜色创建多少个高亮集,并高亮对应的实体集
    int width = 4;

    for (auto const &[clr, faceIds] : clrFacesMap) {
      fw::HighlightSetPtr pHiliSet = nullptr;
      auto it = hiliSetsClrMap.find(clr);
      if (it == hiliSetsClrMap.end()) {
        pHiliSet = ApiUtils::FindOrCreateFstHighlightSet(clr, width, true);
        hiliSetsClrMap.insert(std::make_pair(clr, pHiliSet));
      } else {
        pHiliSet = it->second;
      }
      ApiUtils::ThickenAndHighlight(pHiliSet, faceIds);
    }

    return vf_Success;
  } catch (_com_error const &e) {
    BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
    return vf_Failure;
  }
  return vf_Success;
}
vfHRESULT vfChangeSurfaceColorFromMap(const char *Surfaces[]) {
  try {
    BOOST_LOG_TRIVIAL(info)
        << LogUtil::GetLogContent(__func__, "Surfaces:", Surfaces);
    // 拿到特征，并把特征删除了
    pt::PartDocumentPtr pPartDoc = SEAPP->GetActiveDocument();
    if (nullptr == pPartDoc) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    // 清空所有高亮集
    for (auto const &[clr, pHiliSet] : hiliSetsClrMap) {
      pHiliSet->Delete();
    }
    hiliSetsClrMap.clear();

    return vf_Success;
  } catch (_com_error const &e) {
    BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
    return vf_Failure;
  }
  return vf_Success;
}

vfHRESULT vfChangePointColorInsertMap(const char *inputStringValue[],
                                      int32_t colorR, int32_t colorG,
                                      int32_t colorB) {
  try {
    BOOST_LOG_TRIVIAL(info) << LogUtil::GetLogContent(
        __func__, "inputStringValue:", inputStringValue, "colorR:", colorR,
        "colorG:", colorG, "colorB:", colorB);
    std::vector<int64_t> ptIds;
    while (*inputStringValue) {
      const char *ptIdStr = *inputStringValue;
      if (ptIdStr == nullptr || ptIdStr[0] == '\0') {
        inputStringValue++;
        continue;
      }
      int64_t ptId = std::stoull(ptIdStr);
      ptIds.push_back(ptId);
      inputStringValue++;
    }
    if (ptIds.empty()) {
      return vf_Success;
    }

    if (FAILED(ApiUtils::ThickenAndHighlight(ptIds, RGB(colorR, colorG, colorB),
                                             4))) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    return vf_Success;
  } catch (_com_error const &e) {
    LogComError(__func__, e);
    BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
    return vf_Failure;
  }
  return vf_Success;
}

vfHRESULT vfChangePointColorFromMap(const char *inputStringValue[]) {
  try {
    BOOST_LOG_TRIVIAL(info) << LogUtil::GetLogContent(
        __func__, "inputStringValue:", inputStringValue);
    std::vector<int64_t> ptIds;
    while (*inputStringValue) {
      const char *ptIdStr = *inputStringValue;
      if (ptIdStr == nullptr || ptIdStr[0] == '\0') {
        inputStringValue++;
        continue;
      }
      int64_t ptId = std::stoull(ptIdStr);
      ptIds.push_back(ptId);
      inputStringValue++;
    }
    if (ptIds.empty()) {
      return vf_Success;
    }
    ApiUtils::ClearThickenAndHighlight(ptIds);
    // 刷新下视图
    fw::WindowPtr pActiveWindow = SEAPP->GetActiveWindow();
    pActiveWindow->GetView()->Update();
  } catch (_com_error const &e) {
    LogComError(__func__, e);
    BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
    return vf_Failure;
  }

  return vf_Success;
}

vfHRESULT vfTestPointOnSurfaceByProject(const char *SurfaceID, double px,
                                        double py, double pz, double dx,
                                        double dy, double dz, int &result) {

  try {
    BOOST_LOG_TRIVIAL(info) << LogUtil::GetLogContent(
        __func__, "SurfaceID:", SurfaceID, "px:", px, "py:", py, "pz:", pz,
        "dx:", dx, "dy:", dy, "dz:", dz);

    int64_t faceId = std::stoull(SurfaceID);
    gm::FacePtr pFace = ApiUtils::FindEntityByID(faceId);
    if (nullptr == pFace) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    ATL::CComSafeArray<double> projPoint =
        ApiUtils::ToComSafeArray({px, py, pz});
    ATL::CComSafeArray<double> projDir = ApiUtils::ToComSafeArray({dx, dy, dz});
    long status;
    if (FAILED(pFace->IsPointInsideProjectRange(
            projPoint.GetSafeArrayPtr(), projDir.GetSafeArrayPtr(), &status))) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }
    result = status;
    return vf_Success;
  } catch (const _com_error &e) {
    LogComError(__func__, e);
    BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
    return vf_Failure;
  }
}

vfHRESULT vfGetIntersectCurve(const char *CurveID,
                              const char **&IntersectCurveID) {
  try {

    BOOST_LOG_TRIVIAL(info)
        << LogUtil::GetLogContent(__func__, "CurveID:", CurveID);
    int64_t idCurve = std::stoull(CurveID);
    gm::EdgePtr pCurve = ApiUtils::FindEntityByID(idCurve);
    if (nullptr == pCurve) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }
    // 获取面
    gm::EdgesPtr pEdges = nullptr;
    if (FAILED(pCurve->GetIntersectCurves(&pEdges)) || pEdges == nullptr) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    int cnt = pEdges->GetCount();
    IntersectCurveID = new const char *[cnt + 1] { nullptr };
    for (long i = 0; i < cnt; i++) {
      gm::EdgePtr pEdge = pEdges->Item(i + 1);
      int64_t edgeId = ApiUtils::GetEntityID(pEdge);
      std::string idStr = std::to_string(edgeId);
      IntersectCurveID[i] = _strdup(idStr.c_str());
    }

    return vf_Success;
  } catch (const _com_error &e) {
    LogComError(__func__, e);
    BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
    return vf_Failure;
  }
}

vfHRESULT vfGeodesicLengthOfPointToCurve(const char *SurfaceID,
                                         const char *CurveID,
                                         const char *PointID, double x,
                                         double y, double z,
                                         double &GeodesicLength) {
  try {
    int64_t idSurface = std::stoull(SurfaceID);
    int64_t idPoint = std::stoull(PointID);
    int64_t idCurve = std::stoull(CurveID);
    BOOST_LOG_TRIVIAL(info) << LogUtil::GetLogContent(
        __func__, "SurfaceID:", SurfaceID, "PointID:", PointID,
        "CurveID:", CurveID, "x:", x, "y:", y, "z:", z);

    IDispatchPtr pFace = ApiUtils::FindEntityByID(idSurface);
    IDispatchPtr pEdge = ApiUtils::FindEntityByID(idCurve);
    gm::VertexPtr pVertex = ApiUtils::FindEntityByID(idPoint);
    if (nullptr == pFace || nullptr == pEdge || nullptr == pVertex) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    pt::PartDocumentPtr pPartDoc = SEAPP->GetActiveDocument();
    if (nullptr == pPartDoc) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    ATL::CComSafeArray<double> dir = ApiUtils::ToComSafeArray({x, y, z});
    if (FAILED(pPartDoc->CalcGeodesicDistance(
            pFace, pEdge, pVertex, dir.GetSafeArrayPtr(), &GeodesicLength))) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }
    GeodesicLength *= COORD_TO_MM;

    return vf_Success;
  } catch (const _com_error &e) {
    LogComError(__func__, e);
    BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
    return vf_Failure;
  }
}

vfHRESULT vfCreatPointByGeodesicDis(const char *surfaceID, const char *pointID1,
                                    double x, double y, double z,
                                    double geodesicDis, const char *&pointID2) {

  try {
    BOOST_LOG_TRIVIAL(info) << LogUtil::GetLogContent(
        __func__, "surfaceID:", surfaceID, "pointID1:", pointID1, "x:", x,
        "y:", y, "z:", z, "geodesicDis:", geodesicDis);
    geodesicDis *= MM_TO_COORD;

    int64_t idSurface = std::stoull(surfaceID);
    int64_t idPoint = std::stoull(pointID1);
    IDispatchPtr pFace = ApiUtils::FindEntityByID(idSurface);
    gm::VertexPtr pVertex = ApiUtils::FindEntityByID(idPoint);
    if (nullptr == pFace || nullptr == pVertex) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    ATL::CComSafeArray<double> dir = ApiUtils::ToComSafeArray({x, y, z});
    ATL::CComSafeArray<double> resPoint(3);
    ATL::CComSafeArray<double> ptData;
    pVertex->GetPointData(ptData.GetSafeArrayPtr());

    pt::PartDocumentPtr pPartDoc = SEAPP->GetActiveDocument();

    if (FAILED(pPartDoc->CalcGeodesicDisPoint(
            pFace.GetInterfacePtr(), ptData.GetSafeArrayPtr(),
            dir.GetSafeArrayPtr(), geodesicDis, resPoint.GetSafeArrayPtr()))) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }
    double resX = resPoint[0];
    double resY = resPoint[1];
    double resZ = resPoint[2];

    auto status = vfCreatePoint(resX, resY, resZ, pointID2);

    return status;
  } catch (const _com_error &e) {
    LogComError(__func__, e);
    BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
    return vf_Failure;
  }
}

// 专门控制法向箭头的高亮集(法向箭头亮显要求是红色的)
static fw::HighlightSetPtr pNormalArrowHiliset = nullptr;
vfHRESULT vfCreateNormalLineAndArrowhead(const char *SurfaceID,
                                         const char *PointID) {
  try {
    if (SurfaceID[0] == '\0' || PointID[0] == '\0') {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }
    BOOST_LOG_TRIVIAL(info) << LogUtil::GetLogContent(
        __func__, "SurfaceID:", SurfaceID, "PointID:", PointID);

    //***Step 1:获取面上点的法向箭头
    bool bOn = false;
    double x = 0, y = 0, z = 0;
    int64_t surID = std::stoull(SurfaceID);
    int64_t ptID = std::stoull(PointID);

    IDispatchPtr pFaceOrFaces = ApiUtils::FindEntityByID(surID);
    if (nullptr == pFaceOrFaces) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    // 取得点，及点坐标
    gm::VertexPtr pVertex = ApiUtils::FindEntityByID(ptID);
    if (nullptr == pVertex) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }
    ATL::CComSafeArray<double> ptData(3);
    pVertex->GetPointData(ptData.GetSafeArrayPtr());

    // 取得点所在的面
    gm::FacePtr pFace = pFaceOrFaces;
    if (nullptr == pFace) {
      gm::FacesPtr pFaces = pFaceOrFaces;
      pFace =
          ApiUtils::FindPointOnFace(ptData[0], ptData[1], ptData[2], pFaces);
    }

    if (nullptr == pFace) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    // 此点在线上的参数
    LPSAFEARRAY pParam = nullptr;
    LPSAFEARRAY pGuessParam = nullptr;
    LPSAFEARRAY pFlags = nullptr;
    LPSAFEARRAY pMaxDeviations = nullptr;
    if (FAILED(pFace->GetParamAtPoint(1, ptData.GetSafeArrayPtr(), &pGuessParam,
                                      &pMaxDeviations, &pParam, &pFlags))) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }
    DestroySafaArray(pGuessParam);
    DestroySafaArray(pMaxDeviations);
    DestroySafaArray(pFlags);
    ATL::CComSafeArray<double> param;
    param.Attach(pParam);

    ATL::CComSafeArray<double> normalVector(3);
    if (FAILED(pFace->GetNormal(1, param.GetSafeArrayPtr(),
                                normalVector.GetSafeArrayPtr()))) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }
    x = normalVector[0];
    y = normalVector[1];
    z = normalVector[2];
    if (bOn == 1) {
      x = -x;
      y = -y;
      z = -z;
    }

    //***Step 2:创建法向箭头
    pt::PartDocumentPtr pActiveDoc = SEAPP->GetActiveDocument();
    SolidEdgePart::Sketch3DPtr pske3D = ApiUtils::GetFirstSketch3D();
    SolidEdgePart::Lines3DPtr pLines3D = pske3D->Lines3D;

    // 箭头长,及箭头侧边两线长
    double arrowLength = 100 * MM_TO_COORD;

    DVec3d faceNormal{x, y, z};
    faceNormal.normalize();
    // 创建箭簇需要一个平面法向，任先一个平面
    DVec3d arrowPlaneNormal;
    if (faceNormal.dot(DVec3d{0, 0, 1}) > 0) {
      arrowPlaneNormal = faceNormal.cross(DVec3d{0, 0, 1});
    } else {
      arrowPlaneNormal = faceNormal.cross(DVec3d{0, 1, 0});
    }
    arrowPlaneNormal.normalize();

    // 计算并创建线
    DPoint3d ptArrowSt{ptData[0], ptData[1], ptData[2]};
    std::array<DPoint3d, 4> arrowPts =
        CalcArrow(ptArrowSt, arrowLength, faceNormal, arrowPlaneNormal);
    std::vector<DPoint3d> pts{arrowPts[0], arrowPts[1], arrowPts[1],
                              arrowPts[2], arrowPts[1], arrowPts[3]};
    ATL::CComSafeArray<double> pointData(pts.size() * 3);
    for (int i = 0; i < pts.size(); ++i) {
      pointData[i * 3] = pts[i].x();
      pointData[i * 3 + 1] = pts[i].y();
      pointData[i * 3 + 2] = pts[i].z();
    }
    ATL::CComSafeArray<IDispatch *> lineArr(3);
    pLines3D->AddMultiple(3, pointData.GetSafeArrayPtr(),
                          lineArr.GetSafeArrayPtr());

    // 返回ID集并序列化
    std::vector<int64_t> lineIDs(3);
    for (int i = 0; i < 3; ++i) {
      IDispatch *pLine = lineArr[i];
      gm::EdgePtr pEdge = ApiUtils::EntityToGeomtry(pLine);
      lineIDs[i] = ApiUtils::GetEntityID(pEdge);
    }

    VFAdapter::SaveMapping(ptID, lineIDs[0], lineIDs[1], lineIDs[2]);
    if (pNormalArrowHiliset == nullptr) {
      fw::SolidEdgeDocumentPtr pActiveDoc = SEAPP->GetActiveDocument();
      fw::HighlightSetsPtr pHiliSets = pActiveDoc->GetHighlightSets();
      pNormalArrowHiliset = pHiliSets->Add();
      pNormalArrowHiliset->PutColor(RGB(255, 0, 0));
      pNormalArrowHiliset->PutWidth(4);
    }
    for (int i = 0; i < 3; ++i) {
      IDispatch *pLine = lineArr[i];
      pNormalArrowHiliset->AddItem(pLine);
    }
    pNormalArrowHiliset->Draw();

    return vf_Success;
  } catch (const _com_error &e) {
    LogComError(__func__, e);
    BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
    return vf_Failure;
  }
}

vfHRESULT vfDeleteNormalLineAndArrowhead(const char *PointID) {
  try {
    BOOST_LOG_TRIVIAL(info)
        << LogUtil::GetLogContent(__func__, "PointID:", PointID);

    if (nullptr == PointID || '\0' == PointID[0]) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    int64_t ptID = std::stoull(PointID);
    int64_t normalLid = 0, normalLid1 = 0, normalLid2 = 0;
    VFAdapter::LoadMapping(ptID, normalLid, normalLid1, normalLid2);

    std::vector<std::string> ids{std::to_string(normalLid),
                                 std::to_string(normalLid1),
                                 std::to_string(normalLid2)};
    for (auto const &idStr : ids) {
      vfRemoveTargetObject(idStr.c_str());
    }

    if (pNormalArrowHiliset != nullptr) {
      pNormalArrowHiliset->RemoveAll();
      // 刷新下视图
      fw::WindowPtr pActiveWindow = SEAPP->GetActiveWindow();
      pActiveWindow->GetView()->Update();
    }
    return vf_Success;
  } catch (const _com_error &e) {
    LogComError(__func__, e);
    BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
    return vf_Failure;
  }
}

vfHRESULT ClearPersistData() {
  try {
    BOOST_LOG_TRIVIAL(info) << __func__;
    pt::PartDocumentPtr pActiveDoc = SEAPP->GetActiveDocument();
    if (nullptr == pActiveDoc) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }
    pActiveDoc->ClearPersistData();
  } catch (_com_error const &e) {
    LogComError(__func__, e);
    BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
    return vf_Failure;
  }
  return vf_Success;
}

vfHRESULT vfGetActiveDocumentName(BSTR &ActiveDocumentName) {
  try {
    BOOST_LOG_TRIVIAL(info) << __func__;
    pt::PartDocumentPtr pPartDoc = SEAPP->GetActiveDocument();
    if (nullptr == pPartDoc) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    ActiveDocumentName = pPartDoc->Name;
    return vf_Success;
  } catch (_com_error const &e) {
    LogComError(__func__, e);
    BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
    return vf_Failure;
  }
}

//!
//! @brief 对边集进行排序，使不仅按连接顺序，还首尾相连。
//! 原始的边集不一定可达成目的，此时返回原有的顺序。
//! @param resEdges
//! @param pOriginEdges
//!
void SortEdges(std::vector<gm::EdgePtr> &resEdges,
               gm::EdgesPtr const &pOriginEdges) {
  gm::EdgePtr pEdge = pOriginEdges->Item(1l);
  resEdges.push_back(pEdge);

  std::set<long> sortedEdgeIdxSet{0};
  long length = pOriginEdges->GetCount();
  while (resEdges.size() != length) {
    gm::EdgePtr pEdgeLast = resEdges.back();
    gm::VertexPtr pEndVtx = pEdgeLast->GetEndVertex();
    // get connect edges;
    bool findNext = false;
    for (long i = 0; i < length; i++) {
      gm::EdgePtr pEdge = pOriginEdges->Item(i + 1);
      if (sortedEdgeIdxSet.count(i)) {
        continue;
      }
      if (pEdge->GetStartVertex() == pEndVtx) {
        resEdges.push_back(pEdge);
        sortedEdgeIdxSet.insert(i);
        findNext = true;
        break;
      }
    }
    if (!findNext) {
      break;
    }
  }
  bool hasSortedCollect = (resEdges.size() == pOriginEdges->GetCount());
  if (!hasSortedCollect) {
    // 如果排序失败(说明原链本身不是首尾相连的)，直接使用原有的顺序
    resEdges.clear();
    for (long i = 0; i < length; i++) {
      gm::EdgePtr pEdge = pOriginEdges->Item(i + 1);
      resEdges.push_back(pEdge);
    }
  }
};

vfHRESULT vfDismantleCurve(const char *CurveID, const char **&DismantlesID,
                           const char **&LineOrCurve, int &length) {
  try {
    BOOST_LOG_TRIVIAL(info)
        << LogUtil::GetLogContent(__func__, "CurceId:", CurveID);

    int64_t idCurve = std::stoull(CurveID);
    IDispatchPtr pCurve = ApiUtils::FindEntityByID(idCurve);
    if (nullptr == pCurve) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    geom_entity_type geomType = ApiUtils::GetGeomType(pCurve);
    if (geomType != geom_entity_type::vf_curve &&
        geom_entity_type::vf_curves != geomType) {
      // 传入应该是线或线组
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }
    if (geomType == geom_entity_type::vf_curve) {
      // 线时，子曲线就是线
      DismantlesID = new const char *[2]{nullptr};
      LineOrCurve = new const char *[2]{nullptr};
      DismantlesID[0] = CurveID;
      length = 1;

      gm::EdgePtr pEdge = pCurve;
      IDispatchPtr pGeom = pEdge->GetGeometry();
      gm::GNTTypePropertyConstants objType;
      HRESULT hr = ApiUtils::GetLocatedGraphicType(pGeom, (int &)objType);
      if (FAILED(hr)) {
        BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
        return vf_Failure;
      }

      std::string objTypeStr = (gm::igLine == objType ? "Line" : "Curve");
      LineOrCurve[0] = _strdup(objTypeStr.c_str());
    } else if (geomType == geom_entity_type::vf_curves) {
      // 线组
      gm::EdgesPtr pEdges = pCurve;
      length = pEdges->Count;

      DismantlesID = new const char *[length + 1] { nullptr };
      LineOrCurve = new const char *[length + 1] { nullptr };
      std::vector<gm::EdgePtr> resEdges;
      SortEdges(resEdges, pEdges);
      for (long i = 0; i < length; i++) {
        gm::EdgePtr pEdge = resEdges[i];
        if (nullptr == pEdge) {
          BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
          return vf_Failure;
        }
        int64_t edgeId = ApiUtils::GetEntityID(pEdge);
        std::string edgeIdStr = std::to_string(edgeId);
        DismantlesID[i] = _strdup(edgeIdStr.c_str());

        IDispatchPtr pGeom = pEdge->GetGeometry();
        gm::GNTTypePropertyConstants objType;
        HRESULT hr = ApiUtils::GetLocatedGraphicType(pGeom, (int &)objType);
        if (FAILED(hr)) {
          BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
          return vf_Failure;
        }

        std::string objTypeStr = (gm::igLine == objType ? "Line" : "Curve");
        LineOrCurve[i] = _strdup(objTypeStr.c_str());
      }
    }
    BOOST_LOG_TRIVIAL(info) << LogUtil::GetLogContent(
        __func__, "CurceId:", CurveID, "SubCurve:", DismantlesID,
        "LineOrCurve:", LineOrCurve);
    return vf_Success;
  } catch (_com_error const &e) {
    BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
    LogComError(__func__, e);
    return vf_Failure;
  }
}

vfHRESULT vfGetCurveStartAndEndPointCoordinates(const char *CurveID,
                                                double &Startx, double &Starty,
                                                double &Startz, double &Endx,
                                                double &Endy, double &Endz) {
  try {
    BOOST_LOG_TRIVIAL(info)
        << LogUtil::GetLogContent(__func__, "CurceId:", CurveID);
    int64_t idCurve = std::stoull(CurveID);
    IDispatchPtr pCurve = ApiUtils::FindEntityByID(idCurve);
    if (nullptr == pCurve) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    geom_entity_type geomType = ApiUtils::GetGeomType(pCurve);
    if (geomType != geom_entity_type::vf_curve &&
        geom_entity_type::vf_curves != geomType) {
      // 传入应该是线或线组
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    gm::VertexPtr pStVtx = nullptr;
    gm::VertexPtr pEndVtx = nullptr;
    if (geom_entity_type::vf_curve == geomType) {
      // 单根线
      gm::EdgePtr pEdge = pCurve;
      gm::VertexPtr pStVtx = pEdge->GetStartVertex();
      gm::VertexPtr pEndVtx = pEdge->GetEndVertex();
      ATL::CComSafeArray<double> ptStartData(3);
      ATL::CComSafeArray<double> ptEndData(3);
      pStVtx->GetPointData(ptStartData.GetSafeArrayPtr());
      pEndVtx->GetPointData(ptEndData.GetSafeArrayPtr());
      Startx = ptStartData[0] * COORD_TO_MM;
      Starty = ptStartData[1] * COORD_TO_MM;
      Startz = ptStartData[2] * COORD_TO_MM;

      Endx = ptEndData[0] * COORD_TO_MM;
      Endy = ptEndData[1] * COORD_TO_MM;
      Endz = ptEndData[2] * COORD_TO_MM;

    } else if (geom_entity_type::vf_curves == geomType) {
      // 线组时取第一根线的起点，最后一根线的终点
      gm::EdgesPtr pEdges = pCurve;
      auto GetEndVertex = [](gm::EdgePtr pDestEdge,
                             gm::EdgePtr pAdjacentEdge) -> gm::VertexPtr {
        // 返回目标边的端点(非连接点)
        gm::VertexPtr pStVtx = pDestEdge->GetStartVertex();
        gm::VertexPtr pEndVtx = pDestEdge->GetEndVertex();

        gm::VertexPtr pAdjacentStVtx = pAdjacentEdge->GetStartVertex();
        gm::VertexPtr pAdjacentEndVtx = pAdjacentEdge->GetEndVertex();
        if (pAdjacentStVtx == pStVtx || pAdjacentEndVtx == pStVtx) {
          // 目标边起点为连接点时
          return pEndVtx;
        } else if (pAdjacentStVtx == pEndVtx || pAdjacentEndVtx == pEndVtx) {
          //  目标边终点为连接点时
          return pStVtx;
        }
        return nullptr;
      };

      pStVtx = GetEndVertex(pEdges->Item(1l), pEdges->Item(2l));
      pEndVtx = GetEndVertex(pEdges->Item(pEdges->Count),
                             pEdges->Item(pEdges->Count - 1));
      ATL::CComSafeArray<double> ptStartData(3);
      ATL::CComSafeArray<double> ptEndData(3);
      pStVtx->GetPointData(ptStartData.GetSafeArrayPtr());
      pEndVtx->GetPointData(ptEndData.GetSafeArrayPtr());
      Startx = ptStartData[0] * COORD_TO_MM;
      Starty = ptStartData[1] * COORD_TO_MM;
      Startz = ptStartData[2] * COORD_TO_MM;

      Endx = ptEndData[0] * COORD_TO_MM;
      Endy = ptEndData[1] * COORD_TO_MM;
      Endz = ptEndData[2] * COORD_TO_MM;
    }

    std::stringstream ss;
    ss.precision(std::numeric_limits<double>::digits10);
    ss << "Start:" << Startx << "," << Starty << "," << Startz << "End:" << Endx
       << "," << Endy << "," << Endz;

    BOOST_LOG_TRIVIAL(info)
        << LogUtil::GetLogContent(__func__, "Coord:", ss.str());

    return vf_Success;
  } catch (_com_error const &e) {
    LogComError(__func__, e);
    return vf_Failure;
  }
}

vfHRESULT vfGetEquidistantPointsOnCurve(const char *CurveID, int PointNums,
                                        double *&X, double *&Y, double *&Z) {
  try {
    BOOST_LOG_TRIVIAL(info) << LogUtil::GetLogContent(
        __func__, "CurceId:", CurveID, "PointNums:", PointNums);
    int64_t idCurve = std::stoull(CurveID);

    IDispatchPtr pCurve = ApiUtils::FindEntityByID(idCurve);
    if (nullptr == pCurve) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    pt::PartDocumentPtr pPartDoc = SEAPP->GetActiveDocument();
    if (nullptr == pPartDoc) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    ATL::CComSafeArray<double> pts =
        pPartDoc->GetEquidistantPointsOnCurve(pCurve, PointNums);
    int valueSize = pts.GetCount();
    if (valueSize != PointNums * 3) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    BOOST_LOG_TRIVIAL(info) << "double cnt is " << pts.GetCount();
    X = new double[PointNums]{0};
    Y = new double[PointNums]{0};
    Z = new double[PointNums]{0};
    int64_t ptId;
    for (int i = 0; i < PointNums; i++) {
      X[i] = pts[i * 3] * COORD_TO_MM;
      Y[i] = pts[i * 3 + 1] * COORD_TO_MM;
      Z[i] = pts[i * 3 + 2] * COORD_TO_MM;
      // vfCreatePoint(X[i], Y[i], Z[i], ptId);
    }

    BOOST_LOG_TRIVIAL(info) << "Success " << pts.GetCount();
    return vf_Success;
  } catch (_com_error const &e) {
    LogComError(__func__, e);
    return vf_Failure;
  }
}

vfHRESULT vfGetMinimumDistancePointCoordinates(const char *SurfaceID, double X1,
                                               double Y1, double Z1, double &X2,
                                               double &Y2, double &Z2) {
  try {

    BOOST_LOG_TRIVIAL(info) << LogUtil::GetLogContent(
        __func__, "SurfaceID:", SurfaceID, "X1:", X1, "Y1:", Y1, "Z1:", Z1);
    X1 *= MM_TO_COORD;
    Y1 *= MM_TO_COORD;
    Z1 *= MM_TO_COORD;

    int64_t idSurface = std::stoull(SurfaceID);
    IDispatchPtr pSurface = ApiUtils::FindEntityByID(idSurface);
    if (nullptr == pSurface) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }
    long bOn = -1;
    if (vf_Success !=
        GetMinimumDistancePointCoordinates(pSurface, X1, Y1, Z1, bOn)) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    X2 = X1 * COORD_TO_MM;
    Y2 = Y1 * COORD_TO_MM;
    Z2 = Z1 * COORD_TO_MM;
    BOOST_LOG_TRIVIAL(info) << LogUtil::GetLogContent(__func__, "rtX2:", X2,
                                                      "rtY2:", Y2, "rtZ2:", Z2);
    return vf_Success;
  } catch (_com_error const &e) {
    LogComError(__func__, e);
    return vf_Failure;
  }
}

vfHRESULT vfCreateSpline2D(BSTR Name, int Plane, int length,
                           double *PointCoordinate1, double *PointCoordinate2) {
  try {
    // length = 22;
    BOOST_LOG_TRIVIAL(info) << LogUtil::GetLogContent(
        __func__, "Name:", Name, "Plane:", Plane, "length:", length);
    // 首先创建一个二维草图名为Name
    Plane = Plane % 3;

    pt::PartDocumentPtr pPartDoc = SEAPP->GetActiveDocument();
    if (nullptr == pPartDoc) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    pt::RefPlanesPtr pPlanes = pPartDoc->GetRefPlanes();
    pt::RefPlanePtr pPlane = pPlanes->Item((long)Plane);
    pt::SketchsPtr pSketches = pPartDoc->GetSketches();
    pt::SketchPtr pSketch = pSketches->AddByPlane(pPlane);
    try {
      pSketch->PutName(Name);
    } catch (_com_error const &e) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
    }
    pt::ProfilePtr pProfile = pSketch->GetProfile();

    // 创建二维样条曲线
    fwp::BSplineCurves2dPtr pSpl2des = pProfile->GetBSplineCurves2d();

    std::stringstream ss;
    ss.precision(std::numeric_limits<double>::digits10);

    std::vector<DPoint2d> inputPts;
    for (int i = 0; i < length; i++) {
      ss << "\n" << PointCoordinate1[i] << "," << PointCoordinate2[i];
      inputPts.push_back(DPoint2d(PointCoordinate1[i] * MM_TO_COORD,
                                  PointCoordinate2[i] * MM_TO_COORD));
    }

    BOOST_LOG_TRIVIAL(info) << ss.str();
    // 如果首尾点相同，则最终生成的曲线应该是闭合的,采用控制点循环的方式
    if (inputPts.front() == inputPts.back()) {
      DPoint2d ptSt = inputPts.front();
      inputPts.insert(inputPts.begin(), {ptSt, ptSt});
      inputPts.insert(inputPts.end(), {ptSt, ptSt});
      length = inputPts.size();
    }

    // BOOST_LOG_TRIVIAL(info) << "pt2 is " << ptData[2] << " " << ptData[3];
    ATL::CComSafeArray<double> poles(length * 2);
    memcpy((*poles.GetSafeArrayPtr())->pvData, inputPts.data(),
           length * 2 * sizeof(double));

    long degree = 3;
    int length = poles.GetCount() / 2;
    long knotCnt = length + degree + 1;
    ATL::CComSafeArray<double> knots(knotCnt);
    // 构造均匀节点区间

    for (int i = 0; i < degree; i++) {
      knots[i] = 0.0;
      knots[knotCnt - i - 1] = 1.0;
    }

    double avrRange = 1.0 / (knotCnt - 1 - 2 * degree);
    for (int i = degree; i < knotCnt - degree; i++) {
      knots[i] = (i - degree) * avrRange;
    }

    fwp::BSplineCurve2dPtr pSpl2d = pSpl2des->Add(
        degree, length, poles.GetSafeArrayPtr(), knots.GetSafeArrayPtr());

    assert(pSpl2d);

    return vf_Success;
  } catch (_com_error const &e) {
    LogComError(__func__, e);
    return vf_Failure;
  }
}

vfHRESULT vfGetPointUVByCAA(const char *SurfaceID, double X1, double Y1,
                            double Z1, double &X2, double &Y2, double &Z2,
                            double &U, double &V) {
  try {
    BOOST_LOG_TRIVIAL(info) << LogUtil::GetLogContent(
        __func__, "SurfaceID:", SurfaceID, "X1:", X1, "Y1:", Y1, "Z1:", Z1);

    int64_t idSurface = std::stoull(SurfaceID);
    IDispatchPtr pSurface = ApiUtils::FindEntityByID(idSurface);
    if (nullptr == pSurface) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    geom_entity_type geomType = ApiUtils::GetGeomType(pSurface);
    if (geomType != geom_entity_type::vf_face &&
        geom_entity_type::vf_faces != geomType) {
      // 传入应该是面或面组
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    ATL::CComSafeArray<double> pt = ApiUtils::ToComSafeArray({X1, Y1, Z1});
    ATL::CComSafeArray<double> uv(2);
    if (geom_entity_type::vf_face == geomType) {
      // 单个面时
      gm::FacePtr pFace = pSurface;
      long bOn;
      double dis;
      ATL::CComSafeArray<double> uv(2);
      ATL::CComSafeArray<double> closestPt(3);
      if (FAILED(pFace->IsPointOn(pt.GetSafeArrayPtr(), &bOn,
                                  uv.GetSafeArrayPtr(), &dis,
                                  closestPt.GetSafeArrayPtr()))) {
        BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
        return vf_Failure;
      }
      X2 = closestPt[0];
      Y2 = closestPt[1];
      Z2 = closestPt[2];
      U = uv[0];
      V = uv[1];
      return vf_Success;
    } else if (geom_entity_type::vf_faces == geomType) {
      // 面组时
      gm::FacesPtr pFaces = pSurface;
      double minDis = DBL_MAX;
      for (long i = 0; i < pFaces->GetCount(); i++) {
        gm::FacePtr pFace = pFaces->Item(i + 1);
        long bOn;
        double dis;
        ATL::CComSafeArray<double> uv(2);
        ATL::CComSafeArray<double> closestPt(3);
        if (FAILED(pFace->IsPointOn(pt.GetSafeArrayPtr(), &bOn,
                                    uv.GetSafeArrayPtr(), &dis,
                                    closestPt.GetSafeArrayPtr()))) {
          BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
          return vf_Failure;
        }
        if (dis < minDis) {
          minDis = dis;
          X2 = closestPt[0];
          Y2 = closestPt[1];
          Z2 = closestPt[2];
          U = uv[0];
          V = uv[1];
        }
      }

      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    return vf_Success;
  } catch (_com_error const &e) {
    LogComError(__func__, e);
    return vf_Failure;
  }
}

vfHRESULT vfGetEquidistantPointsOnCurveRing(const char *CurveID, int PointNums,
                                            double *&X, double *&Y,
                                            double *&Z) {
  try {
    BOOST_LOG_TRIVIAL(info) << LogUtil::GetLogContent(
        __func__, "CurveID:", CurveID, "PointNums:", PointNums);

    int64_t idCurve = std::stoull(CurveID);
    IDispatchPtr pCurve = ApiUtils::FindEntityByID(idCurve);
    if (nullptr == pCurve) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    pt::PartDocumentPtr pPartDoc = SEAPP->GetActiveDocument();
    if (nullptr == pPartDoc) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    ATL::CComSafeArray<double> pts =
        pPartDoc->GetEquidistantPointsOnCurve(pCurve, PointNums);

    X = new double[PointNums]{0};
    Y = new double[PointNums]{0};
    Z = new double[PointNums]{0};
    for (int i = 0; i < PointNums; i++) {
      X[i] = pts[i * 3];
      Y[i] = pts[i * 3 + 1];
      Z[i] = pts[i * 3 + 2];
    }

    return vf_Success;
  } catch (_com_error const &e) {
    LogComError(__func__, e);
    return vf_Failure;
  }
}

vfHRESULT TGCreateLineStartDirection(double ptX, double ptY, double ptZ,
                                     double dirX, double dirY, double dirZ,
                                     int64_t &lineID) {
  try {
    return CreateSegment(lineID, DPoint3d{ptX, ptY, ptZ},
                         DPoint3d{ptX + dirX, ptY + dirY, ptZ + dirZ});
  } catch (_com_error const &e) {
    LogComError(__func__, e);
    return vf_Failure;
  }
}

vfHRESULT TGCreateLineStartEnd(double stX, double stY, double stZ, double enX,
                               double enY, double enZ, int64_t &lineID) {

  try {
    return CreateSegment(lineID, DPoint3d{stX, stY, stZ},
                         DPoint3d{enX, enY, enZ});
  } catch (_com_error const &e) {
    LogComError(__func__, e);
    return vf_Failure;
  }
}

vfHRESULT vfCreateSurfaceByFill(const char *Curves[], const char *Surface1,
                                const char *&Surface2) {
  try {
    // get args
    BOOST_LOG_TRIVIAL(info) << LogUtil::GetLogContent(
        __func__, "Curves:", Curves, "Surface1:", Surface1);
    std::vector<std::int64_t> edgeIds = ApiUtils::CollectIds(Curves);
    if (edgeIds.empty()) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }
    ATL::CComSafeArray<IDispatch *> edgeDispatchArray;
    for (int64_t edgeId : edgeIds) {
      gm::EdgePtr pEdge = ApiUtils::FindEntityByID(edgeId);
      if (nullptr == pEdge) {
        BOOST_LOG_TRIVIAL(error) << "one of the edge is nullptr";
        BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
        return vf_Failure;
      }
      edgeDispatchArray.Add(pEdge);
    }

    pt::PartDocumentPtr pPartDoc = SEAPP->GetActiveDocument();
    if (nullptr == pPartDoc) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }
    pt::ConstructionsPtr pConstructions = pPartDoc->GetConstructions();
    if (nullptr == pConstructions || pConstructions->GetCount() == 0) {
      BOOST_LOG_TRIVIAL(error)
          << "pConstructions is nullptr,or there is no  construction model";
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    pt::ConstructionModelPtr pConstructionModel = pConstructions->Item(1l);
    if (nullptr == pConstructionModel) {
      BOOST_LOG_TRIVIAL(error) << "pConstructionModel is nullptr";
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }
    ATL::CComSafeArray<IDispatch *> loopDispatchArray;
    ATL::CComSafeArray<IDispatch *> faceDispatchArray;
    pt::SplitFacesPtr pSplitFaces = pConstructionModel->GetSplitFaces();
    pt::SplitFacePtr pSplitFace = pSplitFaces->FindOrCreateLoopAndFace(
        edgeDispatchArray.GetCount(), edgeDispatchArray.GetSafeArrayPtr(),
        faceDispatchArray.GetSafeArrayPtr(),
        loopDispatchArray.GetSafeArrayPtr());
    if (nullptr == pSplitFace) {
      BOOST_LOG_TRIVIAL(error) << "pSplitFace is nullptr";
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }
    fw::HighlightSetPtr pHiliSet = ApiUtils::FindOrCreateFstHighlightSet();

    gm::FacesPtr pFaces = pSplitFace->GetFaces(gm::igQueryAll);
    for (long i = 0; i < pFaces->GetCount(); i++) {
      gm::FacePtr pFace = pFaces->Item(i + 1);
      pHiliSet->AddItem(pFace);
    }
    pHiliSet->Draw();

    int64_t faceId = ApiUtils::GetEntityID(pFaces);
    std::string resFacesId = std::to_string(faceId);
    Surface2 = _strdup(resFacesId.c_str());
    return vf_Success;

  } catch (_com_error const &e) {
    BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
    return vf_Failure;
  }
  return vf_Success;
}

vfHRESULT vfOffsetCurvesByParallel(const char *SurfaceID, const char *CurveID,
                                   double x, double y, double z,
                                   int DistanceNums, double *GeodesicDistances,
                                   BSTR GeometricSetName,
                                   BSTR *GeodesicCurvesName,
                                   const char **&OffsetCurves) {
  try {
    // 批量平行偏置
    BOOST_LOG_TRIVIAL(info) << LogUtil::GetLogContent(
        __func__, "SurfaceID:", SurfaceID, "x:", x, "y:", y, "z:", z,
        "CurveID:", CurveID, "DistanceNums:", DistanceNums);

    pt::PartDocumentPtr pPartDoc = SEAPP->GetActiveDocument();
    pt::Sketch3DPtr pSketch3d = pPartDoc->GetSketches3D()->Add();
    if (nullptr == pPartDoc || SurfaceID == nullptr || SurfaceID[0] == '\0' ||
        CurveID == nullptr || CurveID[0] == '\0') {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    int64_t surfaceId = std::stoull(SurfaceID);
    int64_t curveId = std::stoull(CurveID);

    IDispatchPtr pFaceOrFace = ApiUtils::FindEntityByID(surfaceId);
    IDispatchPtr pEdgeOrEdges = ApiUtils::FindEntityByID(curveId);
    if (nullptr == pFaceOrFace || nullptr == pEdgeOrEdges) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    // 取起点在此面上的法向，及曲线在此点的切向
    auto GetFirstEdgeFirstVertex = [](IDispatchPtr const &pEdgeOrEdges,
                                      gm::VertexPtr &pFstVtx,
                                      gm::EdgePtr &pFstEdge) -> bool {
      geom_entity_type geoType = ApiUtils::GetGeomType(pEdgeOrEdges);
      if (geoType == vf_curve) {
        // 单个边
        pFstEdge = pEdgeOrEdges;
        pFstVtx = pFstEdge->GetStartVertex();
        return true;
      } else if (geoType == vf_curves) {
        gm::EdgesPtr pEdges = pEdgeOrEdges;
        pFstEdge = pEdges->Item(1l);
        pFstVtx = pFstEdge->GetStartVertex();
        return true;
      }
      return false;
    };

    // 取曲线起点在面上的切向 和法向
    gm::VertexPtr pFstVertex = nullptr;
    gm::EdgePtr pFstEdge = nullptr;
    if (!GetFirstEdgeFirstVertex(pEdgeOrEdges, pFstVertex, pFstEdge)) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    int64_t fstCurveId = ApiUtils::GetEntityID(pFstEdge);
    std::string fstCurveIdStr = std::to_string(fstCurveId);
    int64_t fstPointId = ApiUtils::GetEntityID(pFstVertex);
    std::string fstPointIdStr = std::to_string(fstPointId);
    DVec3d normal, tangent;
    VFAdapter::vfHRESULT status = vfGetUnitTangentVectorAtPoint(
        fstCurveIdStr.c_str(), fstPointIdStr.c_str(), false, tangent.x(),
        tangent.y(), tangent.z());
    if (status != vf_Success) {
      return status;
    }

    status =
        vfGetUnitNormalVectorAtPoint(SurfaceID, fstPointIdStr.c_str(), false,
                                     normal.x(), normal.y(), normal.z());
    if (status != vf_Success) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return status;
    }

    DVec3d dirJustify = normal.cross(tangent);
    DVec3d dirOffset{x, y, z};
    // true时，用曲面法向判定;false时，用曲面反向法向判定
    bool isPositive = true;
    if (dirJustify.dot(dirOffset) < 0) {
      isPositive = false;
    }

    // 收集偏移数组
    std::vector<double> offsetDistances(DistanceNums);
    for (int i = 0; i < DistanceNums; i++) {
      offsetDistances[i] = GeodesicDistances[i] * MM_TO_COORD;
    }

    ATL::CComSafeArray<double> headOfsVals(offsetDistances.size());
    memcpy((*headOfsVals.GetSafeArrayPtr())->pvData, offsetDistances.data(),
           offsetDistances.size() * sizeof(double));

    // 调用
    pt::BSplineCurves3DPtr pBsplines3d = pSketch3d->GetBSplineCurves3D();
    LPSAFEARRAY offsetedCurves = nullptr;
    HRESULT hr = pBsplines3d->AddParallelCurvesOnFaceTG(
        pFaceOrFace.GetInterfacePtr(), pEdgeOrEdges.GetInterfacePtr(),
        isPositive, true, headOfsVals.GetSafeArrayPtr(), &offsetedCurves);

    // HRESULT hr = pPartDoc->BatchOffsetOnFaceCurve(
    //     pFaceOrFace.GetInterfacePtr(), pEdgeOrEdges.GetInterfacePtr(),
    //     isPositive, headOfsVals.GetSafeArrayPtr(),
    //     headOfsVals.GetSafeArrayPtr(), &offsetedCurves);
    if (FAILED(hr)) {
      return vf_Failure;
    }

    ATL::CComSafeArray<IDispatch *> offsetedCurvesArray;
    offsetedCurvesArray.Attach(offsetedCurves);
    std::vector<int64_t> offsetedCurveIDs;
    for (int i = 0; i < offsetedCurvesArray.GetCount(); i++) {
      IDispatch *pDispatch = offsetedCurvesArray[i];
      pt::BSplineCurve3DPtr pOffsetCv = pDispatch;
      assert(pOffsetCv);
      gm::EdgePtr pEdge = ApiUtils::EntityToGeomtry(pOffsetCv);
      int64_t edgeID = ApiUtils::GetEntityID(pEdge);
      offsetedCurveIDs.push_back(edgeID);
    }
    OffsetCurves = new const char *[offsetedCurveIDs.size() + 1];
    OffsetCurves[offsetedCurveIDs.size()] = nullptr;
    for (size_t i = 0; i < offsetedCurveIDs.size(); i++) {
      std::string offsetedCurveID = std::to_string(offsetedCurveIDs[i]);
      OffsetCurves[i] = _strdup(offsetedCurveID.c_str());
    }
    BOOST_LOG_TRIVIAL(info)
        << LogUtil::GetLogContent(__func__, "OffsetCurves:", OffsetCurves);

    return vf_Success;
  } catch (_com_error const &e) {
    LogComError(__func__, e);
    return vf_Failure;
  }
}

vfHRESULT vfDeleteCurves(const char *Curves[]) {
  try {
    BOOST_LOG_TRIVIAL(info)
        << LogUtil::GetLogContent(__func__, "Curves:", Curves);
    std::vector<int64_t> topoIds = ApiUtils::CollectIds(Curves);
    if (topoIds.empty()) {
      return vf_Failure;
    }

    return TGBatchDeleteEntities(topoIds);
    return vf_Success;
  } catch (_com_error const &e) {
    LogComError(__func__, e);
    return vf_Failure;
  }
}

vfHRESULT vfOffsetCurveByStartPointAndEndPoint(
    const char *SurfaceID, const char *CurveID, const char *Orientation,
    double *StartPointGeodesicDistances, double *EndPointGeodesicDistances,
    const char **&OffsetCurves) {
  try {
    BOOST_LOG_TRIVIAL(info) << LogUtil::GetLogContent(
        __func__, "SurfaceID:", SurfaceID, "CurveID:", CurveID,
        "Orientation:", Orientation,
        "StartPointGeodesicDistances:", StartPointGeodesicDistances,
        "EndPointGeodesicDistances:", EndPointGeodesicDistances);

    pt::PartDocumentPtr pPartDoc = SEAPP->GetActiveDocument();
    if (nullptr == pPartDoc || SurfaceID == nullptr || SurfaceID[0] == '\0' ||
        CurveID == nullptr || CurveID[0] == '\0' || Orientation == nullptr ||
        Orientation[0] == '\0') {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    int64_t surfaceId = std::stoull(SurfaceID);
    int64_t curveId = std::stoull(CurveID);
    std::string orientation = Orientation;
    for (char &ch : orientation) {
      ch = tolower(ch);
    }
    bool isPositive = (orientation == "true");
    IDispatchPtr pFaceOrFace = ApiUtils::FindEntityByID(surfaceId);
    IDispatchPtr pEdgeOrEdges = ApiUtils::FindEntityByID(curveId);
    if (nullptr == pFaceOrFace || nullptr == pEdgeOrEdges) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    // 获取偏移距离
    std::vector<double> headOfsVals, tailOfsVals;
    while (!isnan(*StartPointGeodesicDistances)) {
      headOfsVals.push_back((*StartPointGeodesicDistances) * MM_TO_COORD);
      StartPointGeodesicDistances++;
    }

    while (!isnan(*EndPointGeodesicDistances)) {
      tailOfsVals.push_back((*EndPointGeodesicDistances) * MM_TO_COORD);
      EndPointGeodesicDistances++;
    }

    ATL::CComSafeArray<double> headOfsValsSafeArr(headOfsVals.size());
    memcpy((*headOfsValsSafeArr.GetSafeArrayPtr())->pvData, headOfsVals.data(),
           headOfsVals.size() * sizeof(double));

    ATL::CComSafeArray<double> tailOfsValsSafeArr(tailOfsVals.size());
    memcpy((*tailOfsValsSafeArr.GetSafeArrayPtr())->pvData, tailOfsVals.data(),
           tailOfsVals.size() * sizeof(double));

    LPSAFEARRAY offsetedCurves = nullptr;
    HRESULT hr = pPartDoc->BatchOffsetOnFaceCurve(
        pFaceOrFace.GetInterfacePtr(), pEdgeOrEdges.GetInterfacePtr(),
        isPositive, headOfsValsSafeArr.GetSafeArrayPtr(),
        tailOfsValsSafeArr.GetSafeArrayPtr(), &offsetedCurves);
    if (offsetedCurves) {
      SafeArrayDestroy(offsetedCurves);
    }
    if (FAILED(hr)) {
      return vf_Failure;
    }

    return vf_Success;
  } catch (_com_error const &e) {
    LogComError(__func__, e);
    return vf_Failure;
  }
}

vfHRESULT vfOffsetCurvesBySymmetry(const char *SurfaceID, const char *CurveID,
                                   double *GeodesicDistances,
                                   const char **&OffsetCurves) {
  try {
    BOOST_LOG_TRIVIAL(info) << LogUtil::GetLogContent(
        __func__, "SurfaceID:", SurfaceID, "CurveID:", CurveID,
        "GeodesicDistances:", GeodesicDistances);
    pt::PartDocumentPtr pPartDoc = SEAPP->GetActiveDocument();
    if (nullptr == pPartDoc || SurfaceID == nullptr || SurfaceID[0] == '\0' ||
        CurveID == nullptr || CurveID[0] == '\0') {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }
    int64_t surfaceId = std::stoull(SurfaceID);
    int64_t curveId = std::stoull(CurveID);
    IDispatchPtr pFaceOrFace = ApiUtils::FindEntityByID(surfaceId);
    IDispatchPtr pEdgeOrEdges = ApiUtils::FindEntityByID(curveId);
    if (nullptr == pFaceOrFace || nullptr == pEdgeOrEdges) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }
    std::vector<double> offsetDistances;
    while (!isnan(*GeodesicDistances)) {
      offsetDistances.push_back((*GeodesicDistances) * MM_TO_COORD);
      GeodesicDistances++;
    }

    if (offsetDistances.empty()) {
      return vf_Failure;
    }

    // 对称偏置，构造另一边的偏移值
    int ofsValCnt = offsetDistances.size();
    for (int i = 0; i < ofsValCnt; i++) {
      offsetDistances.push_back(-offsetDistances[i]);
    }

    ATL::CComSafeArray<double> offsetDistancesSafeArr(offsetDistances.size());
    memcpy((*offsetDistancesSafeArr.GetSafeArrayPtr())->pvData,
           offsetDistances.data(), offsetDistances.size() * sizeof(double));
    LPSAFEARRAY offsetedCurves = nullptr;
    HRESULT hr = pPartDoc->BatchOffsetOnFaceCurve(
        pFaceOrFace.GetInterfacePtr(), pEdgeOrEdges.GetInterfacePtr(), true,
        offsetDistancesSafeArr.GetSafeArrayPtr(),
        offsetDistancesSafeArr.GetSafeArrayPtr(), &offsetedCurves);
    if (offsetedCurves) {
      SafeArrayDestroy(offsetedCurves);
    }
    if (FAILED(hr)) {
      return vf_Failure;
    }
    return vf_Success;
  } catch (_com_error const &e) {
    LogComError(__func__, e);
    return vf_Failure;
  }
}

vfHRESULT vfOffsetCurveByStartPointAndEndPointBySymmetry(
    const char *SurfaceID, const char *CurveID,
    double *StartPointGeodesicDistances, double *EndPointGeodesicDistances,
    const char **&OffsetCurves) {
  try {
    BOOST_LOG_TRIVIAL(info) << LogUtil::GetLogContent(
        __func__, "SurfaceID:", SurfaceID, "CurveID:", CurveID,
        "StartPointGeodesicDistances:", StartPointGeodesicDistances,
        "EndPointGeodesicDistances:", EndPointGeodesicDistances);
    pt::PartDocumentPtr pPartDoc = SEAPP->GetActiveDocument();
    if (nullptr == pPartDoc || SurfaceID == nullptr || SurfaceID[0] == '\0' ||
        CurveID == nullptr || CurveID[0] == '\0') {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }
    int64_t surfaceId = std::stoull(SurfaceID);
    int64_t curveId = std::stoull(CurveID);
    IDispatchPtr pFaceOrFace = ApiUtils::FindEntityByID(surfaceId);
    IDispatchPtr pEdgeOrEdges = ApiUtils::FindEntityByID(curveId);
    if (nullptr == pFaceOrFace || nullptr == pEdgeOrEdges) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    std::vector<double> headOfsVals, tailOfsVals;
    while (!isnan(*StartPointGeodesicDistances)) {
      headOfsVals.push_back((*StartPointGeodesicDistances) * MM_TO_COORD);
      StartPointGeodesicDistances++;
    }
    while (!isnan(*EndPointGeodesicDistances)) {
      tailOfsVals.push_back((*EndPointGeodesicDistances) * MM_TO_COORD);
      EndPointGeodesicDistances++;
    }
    if (headOfsVals.empty() || headOfsVals.size() != tailOfsVals.size()) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    // 对称偏置，构造另一边的偏移值
    int ofsValCnt = headOfsVals.size();
    for (int i = 0; i < ofsValCnt; i++) {
      headOfsVals.push_back(-headOfsVals[i]);
      tailOfsVals.push_back(-tailOfsVals[i]);
    }

    ATL::CComSafeArray<double> headOfsValsSafeArr(headOfsVals.size());
    memcpy((*headOfsValsSafeArr.GetSafeArrayPtr())->pvData, headOfsVals.data(),
           headOfsVals.size() * sizeof(double));
    ATL::CComSafeArray<double> tailOfsValsSafeArr(tailOfsVals.size());
    memcpy((*tailOfsValsSafeArr.GetSafeArrayPtr())->pvData, tailOfsVals.data(),
           tailOfsVals.size() * sizeof(double));

    LPSAFEARRAY offsetedCurves = nullptr;
    HRESULT hr = pPartDoc->BatchOffsetOnFaceCurve(
        pFaceOrFace.GetInterfacePtr(), pEdgeOrEdges.GetInterfacePtr(), true,
        headOfsValsSafeArr.GetSafeArrayPtr(),
        tailOfsValsSafeArr.GetSafeArrayPtr(), &offsetedCurves);
    if (offsetedCurves) {
      SafeArrayDestroy(offsetedCurves);
    }
    if (FAILED(hr)) {
      return vf_Failure;
    }

    return vf_Success;
  } catch (_com_error const &e) {
    LogComError(__func__, e);
    return vf_Failure;
  }
}

vfHRESULT vfDeleteSurfaces(const char *Surfaces[]) {
  try {
    BOOST_LOG_TRIVIAL(info)
        << LogUtil::GetLogContent(__func__, "Surfaces:", Surfaces);
    std::vector<int64_t> topoIds = ApiUtils::CollectIds(Surfaces);
    if (topoIds.empty()) {
      return vf_Failure;
    }

    return TGBatchDeleteEntities(topoIds);
  } catch (_com_error const &e) {
    LogComError(__func__, e);
    return vf_Failure;
  }
}

vfHRESULT TGBatchDeleteEntities(std::vector<int64_t> const &entIds) {
  try {
    // 抑制重计算(刷新)
    SEAPP->PutDelayCompute(true);
    for (auto entId : entIds) {
      std::string entIdStr = std::to_string(entId);
      vfRemoveTargetObject(entIdStr.c_str());
    }
    // 恢复重计算(刷新)
    SEAPP->PutDelayCompute(false);
    return vf_Success;
  } catch (_com_error const &e) {
    LogComError(__func__, e);
    return vf_Failure;
  }
}

vfHRESULT vfTrimOrExtendCurvesFromEndPoint(const char *Curves[],
                                           const char *Surface, double *X,
                                           double *Y, double *Z,
                                           double *Offsetength) {
  try {
    BOOST_LOG_TRIVIAL(info) << LogUtil::GetLogContent(
        __func__, "Surface:", Surface, "X:", X, "Y:", Y, "Z:", Z,
        "Offsetength:", Offsetength);

    pt::PartDocumentPtr pActiveDoc = SEAPP->GetActiveDocument();
    if (nullptr == pActiveDoc || Surface == nullptr || Surface[0] == '\0') {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    int64_t surfaceId = std::stoull(Surface);
    IDispatchPtr pFaceOrFaces = ApiUtils::FindEntityByID(surfaceId);
    if (nullptr == pFaceOrFaces) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    std::vector<double> xArr, yArr, zArr, offsetVals;
    while (!isnan(*X)) {
      xArr.push_back(*X);
      X++;
    }
    while (!isnan(*Y)) {
      yArr.push_back(*Y);
      Y++;
    }
    while (!isnan(*Z)) {
      zArr.push_back(*Z);
      Z++;
    }
    while (!isnan(*Offsetength)) {
      offsetVals.push_back(*Offsetength);
      Offsetength++;
    }

    if (xArr.empty() || xArr.size() != yArr.size() ||
        xArr.size() != zArr.size() || xArr.size() != offsetVals.size()) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    int cvSize = xArr.size();
    // 传入多少数值，应有多少条线
    pt::Sketch3DPtr pSketch3d = nullptr;
    ATL::CComSafeArray<IDispatch *> line3dArr(cvSize);
    for (int i = 0; i < cvSize; ++i) {
      const char *cvIdStr = Curves[i];
      if (cvIdStr == nullptr || cvIdStr[0] == '\0') {
        BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
        return vf_Failure;
      }
      int64_t cvId = std::stoull(cvIdStr);
      gm::EdgePtr pEdge = ApiUtils::FindEntityByID(cvId);
      if (nullptr == pEdge) {
        BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
        return vf_Failure;
      }
      IDispatchPtr pParent = pEdge->GetParent();
      pt::Line3DPtr pLine = pParent;
      // 必须是三维直线
      if (nullptr == pParent || nullptr == pLine) {
        BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
        return vf_Failure;
      }
      if (nullptr == pSketch3d) {
        pSketch3d = pLine->GetParent();
      }
      line3dArr[i] = pParent.GetInterfacePtr();
    }

    // 构造端点,
    ATL::CComSafeArray<double> endPts(cvSize * 3);
    for (int i = 0; i < cvSize; ++i) {
      endPts[i * 3] = xArr[i] * MM_TO_COORD;
      endPts[i * 3 + 1] = yArr[i] * MM_TO_COORD;
      endPts[i * 3 + 2] = zArr[i] * MM_TO_COORD;
    }

    std::for_each(offsetVals.begin(), offsetVals.end(),
                  [&](double &val) { val *= MM_TO_COORD; });
    ATL::CComSafeArray<double> extendLenArr(cvSize);
    memcpy((*extendLenArr.GetSafeArrayPtr())->pvData, offsetVals.data(),
           cvSize * sizeof(double));

    HRESULT hr =
        pSketch3d->Extend(line3dArr.GetSafeArrayPtr(), endPts.GetSafeArrayPtr(),
                          extendLenArr.GetSafeArrayPtr(), cvSize);
    if (FAILED(hr)) {
      return vf_Failure;
    }

    return vf_Success;
  } catch (_com_error const &e) {
    LogComError(__func__, e);
    return vf_Failure;
  }
}

vfHRESULT vfTrimOrExtendCurvesFromEndPointOnSurface(const char *Curves[],
                                                    const char *Surface,
                                                    double *X, double *Y,
                                                    double *Z,
                                                    double *Offsetength) {
  try {
    BOOST_LOG_TRIVIAL(info)
        << LogUtil::GetLogContent(__func__, "Surface:", Surface);

    pt::PartDocumentPtr pActiveDoc = SEAPP->GetActiveDocument();
    auto funGetFaceByEntityId = [](const char *surface) -> gm::FacePtr {
      int64_t idSurface = std::stoull(surface);
      IDispatchPtr pSurface = ApiUtils::FindEntityByID(idSurface);
      if (nullptr == pSurface) {
        BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
        return nullptr;
      }
      geom_entity_type geomType = ApiUtils::GetGeomType(pSurface);
      if (geomType == geom_entity_type::vf_faces) {
        // 仅支持单面
        gm::FacesPtr pFaces = pSurface;
        auto cnt = pFaces->GetCount();
        if (cnt < 1) {
          BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
          return nullptr;
        } else {
          return pFaces->Item(1);
        }
      } else if (geomType == geom_entity_type::vf_face) {
        return pSurface;
      } else {
        BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
        return nullptr;
      }
    };

    std::vector<int64_t> vctEdgeId;
    {
      while (*Curves) {
        const char *pChar = *Curves;
        std::string idStr = pChar;
        if (idStr.empty()) {
          Curves++;
          continue;
        } else {
          int64_t idCurve = std::stoull(idStr);
          vctEdgeId.push_back(idCurve);
          Curves++;
        }
      }
    }
    std::vector<DPoint3d> endPoints;
    {
      double ptX, ptY, ptZ;

      // 收集点集,后续可以不收集直接生成曲线，此处收集为了便于调试。
      while (!isnan(*X)) {
        ptX = (*X++) * MM_TO_COORD;
        ptY = (*Y++) * MM_TO_COORD;
        ptZ = (*Z++) * MM_TO_COORD;
        if (ptX != DBL_MAX) {
          endPoints.push_back(DPoint3d{ptX, ptY, ptZ});
        }
      }
    }
    std::vector<double> vctOffset;
    {
      while (!isnan(*Offsetength)) {
        vctOffset.push_back((*Offsetength++) * MM_TO_COORD);
      }
    }
    gm::FacePtr ptrFace = nullptr;
    {
      ptrFace = funGetFaceByEntityId(Surface);
      if (nullptr == ptrFace) {
        BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
        return vf_Failure;
      }
    }

    if (vctEdgeId.size() != endPoints.size() ||
        vctEdgeId.size() != vctOffset.size()) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    for (int i = 0; i < vctEdgeId.size(); ++i) {
      int64_t cvId = vctEdgeId[i];
      DPoint3d point = endPoints[i];
      double dOffsetLength = vctOffset[i];

      IDispatchPtr ptrEnt = ApiUtils::FindEntityByID(cvId);
      int64_t featureId = ApiUtils::GetEdgeSegmentID(ptrEnt);
      pt::Line3DPtr ptrLine3D = pActiveDoc->FindObjectByID(-1 * featureId);

      if (nullptr == ptrLine3D || ptrLine3D->GetType() != cs::igLine3D) {
        BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
        return vf_Failure;
      }
      DPoint3d ptLineStart, ptLineEnd;
      DVec3d vecLineUnit;
      {
        ATL::CComSafeArray<double> ptStart(3), ptEnd(3);
        ptrLine3D->GetKeypointPosition(
            SolidEdgePart::Sketch3DKeypointType::igSketch3DStartPoint,
            ptStart.GetSafeArrayPtr());
        ptrLine3D->GetKeypointPosition(
            SolidEdgePart::Sketch3DKeypointType::igSketch3DEndPoint,
            ptEnd.GetSafeArrayPtr());
        vecLineUnit = {ptEnd[0] - ptStart[0], ptEnd[1] - ptStart[1],
                       ptEnd[2] - ptStart[2]};
        vecLineUnit.normalize();
        ptLineStart = {ptStart[0], ptStart[1], ptStart[2]};
        ptLineEnd = {ptEnd[0], ptEnd[1], ptEnd[2]};
      }

      double dDisStartSQ = (point - ptLineStart).norm();
      double dDisEndSQ = (point - ptLineEnd).norm();
      // 误差大于0.01mm认为输入错误，不进行偏移
      if (dDisStartSQ >= 0.00001 && dDisEndSQ >= 0.00001) {
        BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
        return vf_Failure;
      }

      bool bMoveStartPoint = dDisStartSQ <= dDisEndSQ;

      DVec3d vecMoveDir = bMoveStartPoint ? vecLineUnit * -dOffsetLength
                                          : vecLineUnit * dOffsetLength;
      ATL::CComSafeArray<double> ptBeforeMove(3);
      ptBeforeMove[0] = bMoveStartPoint ? ptLineStart[0] : ptLineEnd[0];
      ptBeforeMove[1] = bMoveStartPoint ? ptLineStart[1] : ptLineEnd[1];
      ptBeforeMove[2] = bMoveStartPoint ? ptLineStart[2] : ptLineEnd[2];

      DPoint3d pointTarget =
          bMoveStartPoint ? ptLineStart + vecMoveDir : ptLineEnd + vecMoveDir;

      ATL::CComSafeArray<double> ptNew(3), ptNewOnSurface(3);
      ptNew[0] = pointTarget[0];
      ptNew[1] = pointTarget[1];
      ptNew[2] = pointTarget[2];

      long bOn[1];
      double dDis[1];
      ATL::CComSafeArray<double> uvParam(2);
      // 若偏移后点超出曲面外，则默认偏移到曲面上。
      // IsPointOn会变更原ptrFace地址，需重新获取
      ptrFace->IsPointOn(ptNew.GetSafeArrayPtr(), bOn,
                         uvParam.GetSafeArrayPtr(), dDis,
                         ptNewOnSurface.GetSafeArrayPtr());
      ptrFace = funGetFaceByEntityId(Surface);
      if (nullptr == ptrFace) {
        BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
        return vf_Failure;
      }

      pt::Sketch3DPtr ptrSketch3D = ptrLine3D->GetParent();
      if (nullptr == ptrSketch3D) {
        BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
        return vf_Failure;
      }
      // 重新计算extendLength
      DPoint3d pointNewOnSurface = {ptNewOnSurface[0], ptNewOnSurface[1],
                                    ptNewOnSurface[2]};
      DVec3d vecEndMove = pointNewOnSurface - pointTarget;
      double dExtendLengthNew = vecLineUnit.dot(vecEndMove);
      ATL::CComSafeArray<double> sfarExtendLength(1);
      sfarExtendLength[0] = dExtendLengthNew;

      ATL::CComSafeArray<IDispatch *> sfarLine(1);
      sfarLine[0] = ptrLine3D;
      ptrSketch3D->Extend(sfarLine.GetSafeArrayPtr(),
                          ptBeforeMove.GetSafeArrayPtr(),
                          sfarExtendLength.GetSafeArrayPtr(), 1);
    }

    return vf_Success;
  } catch (_com_error const &e) {
    LogComError(__func__, e);
    return vf_Failure;
  }
}

vfHRESULT vfTestPointAndBoundary(double x, double y, double z,
                                 const char *SurfaceID, const char *BoundaryID,
                                 int &result) {

  try {
    BOOST_LOG_TRIVIAL(info) << LogUtil::GetLogContent(
        __func__, "BoundaryID:", BoundaryID, "SurfaceID:", SurfaceID, "x:", x,
        "y:", y, "z:", z);

    int64_t boundaryId = std::stoull(BoundaryID);
    int64_t surfaceId = std::stoull(SurfaceID);

    pt::PartDocumentPtr pPartDoc = SEAPP->GetActiveDocument();
    if (nullptr == pPartDoc) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    IDispatchPtr pSurface = ApiUtils::FindEntityByID(surfaceId);
    IDispatchPtr pBoundary = ApiUtils::FindEntityByID(boundaryId);
    if (nullptr == pSurface || nullptr == pBoundary) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    long res;
    ATL::CComSafeArray<double> ptData = ApiUtils::ToComSafeArray(
        {x * MM_TO_COORD, y * MM_TO_COORD, z * MM_TO_COORD});
    pPartDoc->IsPointOnRegion(pSurface.GetInterfacePtr(),
                              pBoundary.GetInterfacePtr(), VARIANT_FALSE,
                              ptData.GetSafeArrayPtr(), &res);
    result = res;
    std::string logStr;
    if (0 == result) {
      logStr = "点在边界外";
    } else if (1 == result) {
      logStr = "点在边界内";
    } else if (2 == result) {
      logStr = "点在边界的边界线上";
    }
    BOOST_LOG_TRIVIAL(info) << logStr;

    return vf_Success;
  } catch (_com_error const &e) {
    LogComError(__func__, e);
    return vf_Failure;
  }
}

vfHRESULT vfDataTableSave(BSTR dbPath) {
  try {
    _bstr_t strPath = dbPath;
    BOOST_LOG_TRIVIAL(info) << LogUtil::GetLogContent(
        __func__, "dbPath:", strPath.operator const char *());

    // 获取当前活动文档
    IDispatchPtr pActiveDoc = SEAPP->GetActiveDocument();
    if (!pActiveDoc) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    pt::PartDocumentPtr pPartDoc = pActiveDoc;
    if (!pPartDoc) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    std::vector<BYTE> vctBytes;
    if (!LoadAsBytesFromFile(dbPath, vctBytes)) {
      VF_BOOST_LOG_TRIVAL_LINE;
      return vf_Failure;
    }

    // 打开或创建存储对象
    IStoragePtr pRootStg = nullptr;
    pRootStg = pPartDoc->GetAddInsStorage(
        OLESTR("DBFile"),
        STGM_DIRECT | STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE);

    assert(pRootStg != nullptr);
    HRESULT hr = S_OK;

    // 创建流对象

    IStreamPtr pStreamSize = nullptr;
    hr = pRootStg->CreateStream(OLESTR("DBStreamSize"),
                                STGM_DIRECT | STGM_CREATE | STGM_WRITE |
                                    STGM_SHARE_EXCLUSIVE,
                                0, 0, &pStreamSize);
    if (FAILED(hr) || !pStreamSize) {
      VF_BOOST_LOG_TRIVAL_LINE;
      return vf_Failure;
    }

    // 写入数据到流
    ULONG sizeWritten = 0;
    uint64_t ullStreamSize = vctBytes.size() * sizeof(BYTE);
    hr = pStreamSize->Write(&ullStreamSize, sizeof(uint64_t), &sizeWritten);
    if (FAILED(hr)) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }
    hr = pStreamSize->Commit(STGC_DEFAULT | STGC_OVERWRITE);
    if (FAILED(hr)) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    IStreamPtr pStreamFile = nullptr;

    hr = pRootStg->CreateStream(OLESTR("DBBytesStream"),
                                STGM_DIRECT | STGM_CREATE | STGM_WRITE |
                                    STGM_SHARE_EXCLUSIVE,
                                0, 0, &pStreamFile);
    if (FAILED(hr) || !pStreamFile) {
      VF_BOOST_LOG_TRIVAL_LINE;
      return vf_Failure;
    }
    ULONG bytesWritten = 0;
    hr = pStreamFile->Write(vctBytes.data(), vctBytes.size() * sizeof(BYTE),
                            &bytesWritten);
    if (FAILED(hr)) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }
    hr = pStreamFile->Commit(STGC_DEFAULT | STGC_OVERWRITE);
    if (FAILED(hr)) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    // 提交更改
    hr = pRootStg->Commit(STGC_DEFAULT | STGC_OVERWRITE);
    if (FAILED(hr)) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    // 保存到持久化存储
    IPersistStoragePtr pPersistStorage = pPartDoc;
    hr = pPersistStorage->Save(pRootStg, FALSE);
    if (FAILED(hr)) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }
    return vf_Success;
  } catch (_com_error const &e) {
    LogComError(__func__, e);
    return vf_Failure;
  }
}

vfHRESULT vfDataTableRetrive(BSTR dbPath) {
  try {
    _bstr_t strPath = dbPath;
    BOOST_LOG_TRIVIAL(info) << LogUtil::GetLogContent(
        __func__, "dbPath:", strPath.operator const char *());

    // 获取当前活动文档
    IDispatchPtr pActiveDoc = SEAPP->GetActiveDocument();
    if (!pActiveDoc) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    pt::PartDocumentPtr pPartDoc = pActiveDoc;
    if (!pPartDoc) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    // 打开存储对象
    IStoragePtr pRootStg = nullptr;
    pRootStg = pPartDoc->GetAddInsStorage(
        OLESTR("DBFile"), STGM_DIRECT | STGM_READ | STGM_SHARE_EXCLUSIVE);
    if (!pRootStg) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }
    HRESULT hr = S_OK;
    // 打开流对象
    IStreamPtr pStreamSize = nullptr;
    hr =
        pRootStg->OpenStream(OLESTR("DBStreamSize"), NULL,
                             STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStreamSize);
    if (!pStreamSize || FAILED(hr)) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    // 读取数据
    uint64_t ullSize = 0;
    ULONG bytesRead = 0;
    hr = pStreamSize->Read(&ullSize, sizeof(uint64_t), &bytesRead);
    if (FAILED(hr) || bytesRead != sizeof(uint64_t)) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    // 打开流对象
    IStreamPtr pStreamBytes = nullptr;
    hr = pRootStg->OpenStream(OLESTR("DBBytesStream"), NULL,
                              STGM_READ | STGM_SHARE_EXCLUSIVE, 0,
                              &pStreamBytes);
    if (!pStreamBytes || FAILED(hr)) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }
    // 读取数据
    std::vector<BYTE> vctBytes(ullSize / sizeof(BYTE));
    hr = pStreamBytes->Read(vctBytes.data(), ullSize, &bytesRead);
    if (FAILED(hr) || bytesRead != ullSize) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    if (!OverwriteFileFromBytes(dbPath, vctBytes))
      return vf_Failure;

    return vf_Success;
  } catch (_com_error const &e) {
    LogComError(__func__, e);
    return vf_Failure;
  }
}

vfHRESULT
vfCreatGeodesicCurvebyPoint(const char *BoundaryID, const char *SurfaceID,
                            double px, double py, double pz, double dx,
                            double dy, double dz, BSTR GeodesicCurveName,
                            BSTR GeometricSetName, const char *&NewCurveID) {

  try {
    BOOST_LOG_TRIVIAL(info) << LogUtil::GetLogContent(
        __func__, "BoundaryID:", BoundaryID, "SurfaceID:", SurfaceID, "px:", px,
        "py:", py, "pz:", pz, "dx:", dx, "dy:", dy, "dz:", dz,
        "GeodesicCurveName:", GeodesicCurveName,
        "GeometricSetName:", GeometricSetName);

    int64_t idBoundary = std::stoull(BoundaryID);
    int64_t idSurface = std::stoull(SurfaceID);
    IDispatchPtr pSurface = ApiUtils::FindEntityByID(idSurface);
    IDispatchPtr pBoundary = ApiUtils::FindEntityByID(idBoundary);

    pt::PartDocumentPtr pDoc = SEAPP->GetActiveDocument();
    if (nullptr == pSurface || nullptr == pBoundary || nullptr == pDoc) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    pt::Sketch3DPtr pSketch = pDoc->GetSketches3D()->Add();
    pSketch->PutName(GeometricSetName);

    ATL::CComSafeArray<double> ptSpace = ApiUtils::ToComSafeArray(
        {px * MM_TO_COORD, py * MM_TO_COORD, pz * MM_TO_COORD});
    ATL::CComSafeArray<double> dir = ApiUtils::ToComSafeArray({dx, dy, dz});
    IDispatchPtr pSketch3dCv = pDoc->CreateGeodesicCurveByPoint(
        pSurface.GetInterfacePtr(), pBoundary.GetInterfacePtr(), pSketch,
        ptSpace.GetSafeArrayPtr(), dir.GetSafeArrayPtr());
    if (nullptr == pSketch3dCv) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }
    gm::EdgePtr pEdge = ApiUtils::EntityToGeomtry(pSketch3dCv);
    if (nullptr == pEdge) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    int64_t edgeId = ApiUtils::GetEntityID(pEdge);
    std::string edgeIdStr = std::to_string(edgeId);
    NewCurveID = _strdup(edgeIdStr.c_str());

    return vf_Success;
  } catch (_com_error const &e) {
    LogComError(__func__, e);
    return vf_Failure;
  }
}

vfHRESULT vfGetBoundaryIDbyDivide(const char *OriginalBoundaryID,
                                  const char *SurfaceID, double px, double py,
                                  double pz, const char *DivideCurve1ID,
                                  const char *DivideCurve2ID,
                                  const char *&NewBoundaryID) {

  try {
    BOOST_LOG_TRIVIAL(info) << LogUtil::GetLogContent(
        __func__, "OriginalBoundaryID:", OriginalBoundaryID,
        "SurfaceID:", SurfaceID, "px:", px, "py:", py, "pz:", pz,
        "DivideCurve1ID:", DivideCurve1ID, "DivideCurve2ID:", DivideCurve2ID);
    pt::PartDocumentPtr pDoc = SEAPP->GetActiveDocument();
    if (nullptr == pDoc) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }
    pt::Sketch3DPtr pSketch = pDoc->GetSketches3D()->Add();

    int64_t idBoundary = std::stoull(OriginalBoundaryID);
    int64_t idSurface = std::stoull(SurfaceID);
    int64_t idDivideCurve1 = std::stoull(DivideCurve1ID);

    IDispatchPtr pSurface = ApiUtils::FindEntityByID(idSurface);
    IDispatchPtr pBoundary = ApiUtils::FindEntityByID(idBoundary);
    IDispatchPtr pDivideCurve1 = ApiUtils::FindEntityByID(idDivideCurve1);

    if (nullptr == pSurface || nullptr == pBoundary || nullptr == pDoc ||
        nullptr == pDivideCurve1) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    int64_t idDivideCurve2 = -1;
    if (nullptr != DivideCurve2ID && '\0' != DivideCurve2ID[0]) {
      idDivideCurve2 = std::stoull(DivideCurve2ID);
    }
    IDispatchPtr pDivideCurve2 = nullptr;
    if (idDivideCurve2 != -1) {
      // 第二条分割线是可空的
      pDivideCurve2 = ApiUtils::FindEntityByID(idDivideCurve2);
      if (nullptr == pDivideCurve2) {
        BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
        return vf_Failure;
      }
    }

    ATL::CComSafeArray<double> ptSpace = ApiUtils::ToComSafeArray(
        {px * MM_TO_COORD, py * MM_TO_COORD, pz * MM_TO_COORD});

    LPSAFEARRAY pNewLoop = nullptr;
    HRESULT hr = pSketch->GetBSplineCurves3D()->GetBoundaryCurvesByDivide(
        pBoundary.GetInterfacePtr(), pSurface.GetInterfacePtr(),
        ptSpace.GetSafeArrayPtr(), pDivideCurve1.GetInterfacePtr(),
        pDivideCurve2.GetInterfacePtr(), &pNewLoop);
    if (FAILED(hr)) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    // 得到edges
    gm::EdgesPtr pEdges = ApiUtils::ExtractTopoEdges(pNewLoop);
    if (nullptr == pEdges) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    // 创建派生曲线，以确保有顺序
    int edgeCnt = pEdges->GetCount();
    ATL::CComSafeArray<LPDISPATCH> edgeArr(edgeCnt);
    for (long i = 0; i < edgeCnt; i++) {
      edgeArr[i] = pEdges->Item(i + 1);
    }

    pt::DerivedCurvesPtr pDerivedCvs =
        pDoc->GetConstructions()->GetDerivedCurves();
    pt::DerivedCurvePtr pDerivedCv =
        pDerivedCvs->Add(edgeCnt, edgeArr.GetSafeArrayPtr(),
                         pt::DerivedCurveTypeConstants::igDCComposite);
    pEdges = pDerivedCv->GetEdges(gm::igQueryAll);

    // 得到edgesId
    int64_t loopNewId = ApiUtils::GetEntityID(pEdges);
    std::string loopNewIdStr = std::to_string(loopNewId);
    NewBoundaryID = _strdup(loopNewIdStr.c_str());
    BOOST_LOG_TRIVIAL(info) << "newLoopId is " << loopNewId;
    return vf_Success;
  } catch (_com_error const &e) {
    LogComError(__func__, e);
    return vf_Failure;
  }
}

vfHRESULT vfSaveasStp(BSTR StpName, BSTR StpPath) {
  try {
    BOOST_LOG_TRIVIAL(info) << LogUtil::GetLogContent(
        __func__, "StpName:", StpName, "StpPath:", StpPath);

    pt::PartDocumentPtr pPartDoc = SEAPP->GetActiveDocument();
    if (!pPartDoc) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    _bstr_t saveDir = StpPath;
    _bstr_t saveFullPath = saveDir + StpName + ".stp";
    HRESULT hr = pPartDoc->SaveAs(saveFullPath);
    return (hr == S_OK) ? vf_Success : vf_Failure;
  } catch (_com_error const &e) {
    LogComError(__func__, e);
    return vf_Failure;
  }
}

vfHRESULT vfGetBoundaryIDbyPoint(const char *OriginalBoundaryID,
                                 const char *SurfaceID, double ptX, double ptY,
                                 double ptZ, const char *DivideCurveID[],
                                 const char *&NewBoundaryID,
                                 const char *&NewSurfaceID) {

  try {
    BOOST_LOG_TRIVIAL(info) << LogUtil::GetLogContent(
        __func__, "OriginalBoundaryID:", OriginalBoundaryID,
        "SurfaceID:", SurfaceID, "ptX:", ptX, "ptY:", ptY, "ptZ:", ptZ,
        "DivideCurveID:", DivideCurveID);

    int64_t idSurface = std::stoull(SurfaceID);
    int64_t idBoundary = std::stoull(OriginalBoundaryID);

    IDispatchPtr pSurface = ApiUtils::FindEntityByID(idSurface);
    IDispatchPtr pBoundary = ApiUtils::FindEntityByID(idBoundary);
    pt::PartDocumentPtr pPartDoc = SEAPP->GetActiveDocument();
    if (nullptr == pPartDoc || nullptr == pSurface || nullptr == pBoundary) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }
    std::vector<int64_t> divideCurveIds = ApiUtils::CollectIds(DivideCurveID);
    ATL::CComSafeArray<IDispatch *> saCurves(divideCurveIds.size());
    for (int i = 0; i < divideCurveIds.size(); i++) {
      IDispatchPtr pCurve = ApiUtils::FindEntityByID(divideCurveIds[i]);
      saCurves[i] = pCurve;
    }

    ATL::CComSafeArray<double> ptSpace = ApiUtils::ToComSafeArray(
        {ptX * MM_TO_COORD, ptY * MM_TO_COORD, ptZ * MM_TO_COORD});

    LPSAFEARRAY pEdgeArr = nullptr;
    HRESULT hr = pPartDoc->GetBoundaryByPoint(
        pBoundary.GetInterfacePtr(), pSurface.GetInterfacePtr(),
        ptSpace.GetSafeArrayPtr(), saCurves.GetSafeArrayPtr(), &pEdgeArr);

    ATL::CComSafeArray<IDispatch *> edgeArr;
    edgeArr.Attach(pEdgeArr);

    gm::EdgePtr pFstEdge = IDispatchPtr(edgeArr[0]);
    gm::BodyPtr pBody = pFstEdge->GetBody();
    gm::EdgesPtr pEdges = pBody->CreateCollection(
        gm::TopologyCollectionTypeConstants::seEdgeCollection);
    pEdges->Add(pFstEdge);
    for (int i = 1; i < edgeArr.GetCount(); i++) {
      gm::EdgePtr pEdge = IDispatchPtr(edgeArr[i]);
      pEdges->Add(pEdge);
    }

    int64_t loopNewId = ApiUtils::GetEntityID(pEdges);
    std::string loopNewIdStr = std::to_string(loopNewId);
    NewBoundaryID = _strdup(loopNewIdStr.c_str());

    pt::ConstructionsPtr pConstructions = pPartDoc->GetConstructions();
    pt::ConstructionModelPtr pConstructionModel = pConstructions->Item(1l);
    if (nullptr == pConstructionModel) {
      BOOST_LOG_TRIVIAL(error) << "pConstructionModel is nullptr";
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    LPSAFEARRAY pFaceArr = nullptr;
    LPSAFEARRAY pEdgeNewArr = nullptr;
    pt::SplitFacesPtr pSplitFaces = pConstructionModel->GetSplitFaces();
    pt::SplitFacePtr pSplitFace = pSplitFaces->FindOrCreateLoopAndFace(
        edgeArr.GetCount(), edgeArr.GetSafeArrayPtr(), &pFaceArr, &pEdgeNewArr);
    if (nullptr == pFaceArr) {
      return vf_Failure;
    }

    if (pEdgeNewArr != nullptr) {
      SafeArrayDestroy(pEdgeNewArr);
    }
    if (pFaceArr != nullptr) {
      SafeArrayDestroy(pFaceArr);
    }
    gm::FacesPtr pFaces = pSplitFace->GetFaces(gm::igQueryAll);
    if (pFaces != nullptr && pFaces->GetCount() > 0) {
      int64_t faceNewId = ApiUtils::GetEntityID(pFaces);
      std::string faceNewIdStr = std::to_string(faceNewId);
      NewSurfaceID = _strdup(faceNewIdStr.c_str());
    }
    // NewSurfaceID =
    return vf_Success;
  } catch (_com_error const &e) {
    LogComError(__func__, e);
    return vf_Failure;
  }
}

vfHRESULT vfCreateSlitDart(const char *SurfaceID,
                           const char *OriginalBoundaryID, double length,
                           double ptX1, double ptY1, double ptZ1, double ptX2,
                           double ptY2, double ptZ2,
                           const char *&NewBoundaryID) {

  try {

    BOOST_LOG_TRIVIAL(info) << LogUtil::GetLogContent(
        __func__, "SurfaceID:", SurfaceID,
        "OriginalBoundaryID:", OriginalBoundaryID, "length:", length,
        "ptX1:", ptX1, "ptY1:", ptY1, "ptZ1:", ptZ1, "ptX2:", ptX2,
        "ptY2:", ptY2, "ptZ2:", ptZ2);
    ATL::CComSafeArray<double> ptSlitDartSt = ApiUtils::ToComSafeArray(
        {ptX1 * MM_TO_COORD, ptY1 * MM_TO_COORD, ptZ1 * MM_TO_COORD});

    ATL::CComSafeArray<double> ptSlitDartEn = ApiUtils::ToComSafeArray(
        {ptX2 * MM_TO_COORD, ptY2 * MM_TO_COORD, ptZ2 * MM_TO_COORD});

    int64_t idSurface = std::stoull(SurfaceID);
    int64_t idBoundary = std::stoull(OriginalBoundaryID);

    IDispatchPtr pSurface = ApiUtils::FindEntityByID(idSurface);
    IDispatchPtr pBoundary = ApiUtils::FindEntityByID(idBoundary);
    pt::PartDocumentPtr pPartDoc = SEAPP->GetActiveDocument();
    if (nullptr == pPartDoc || nullptr == pSurface || nullptr == pBoundary) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    pt::Sketch3DPtr pSketch3D = ApiUtils::GetFirstSketch3D();
    LPDISPATCH pSlitDart = nullptr;
    HRESULT hr = pPartDoc->CreateSlitDart(
        pSurface.GetInterfacePtr(), pBoundary.GetInterfacePtr(), pSketch3D,
        length, ptSlitDartSt.GetSafeArrayPtr(), ptSlitDartEn.GetSafeArrayPtr(),
        &pSlitDart);
    if (FAILED(hr)) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
    }

    // 得到edgesId
    NewBoundaryID = ApiUtils::GetEntityIDString(pSlitDart);
    return vf_Success;
  } catch (_com_error const &e) {
    LogComError(__func__, e);
    return vf_Failure;
  }
}

vfHRESULT vfCreateVShapeDart(const char *SurfaceID,
                             const char *OriginalBoundaryID, double ptX1,
                             double ptY1, double ptZ1, double ptX2, double ptY2,
                             double ptZ2, double ptX3, double ptY3, double ptZ3,
                             const char *&NewBoundaryID) {
  try {

    BOOST_LOG_TRIVIAL(info) << LogUtil::GetLogContent(
        __func__, "SurfaceID:", SurfaceID,
        "OriginalBoundaryID:", OriginalBoundaryID, "ptX1:", ptX1, "ptY1:", ptY1,
        "ptZ1:", ptZ1, "ptX2:", ptX2, "ptY2:", ptY2, "ptZ2:", ptZ2,
        "ptX3:", ptX3, "ptY3:", ptY3, "ptZ3:", ptZ3);

    ATL::CComSafeArray<double> ptFst = ApiUtils::ToComSafeArray(
        {ptX1 * MM_TO_COORD, ptY1 * MM_TO_COORD, ptZ1 * MM_TO_COORD});

    ATL::CComSafeArray<double> ptMid = ApiUtils::ToComSafeArray(
        {ptX2 * MM_TO_COORD, ptY2 * MM_TO_COORD, ptZ2 * MM_TO_COORD});

    ATL::CComSafeArray<double> ptLst = ApiUtils::ToComSafeArray(
        {ptX3 * MM_TO_COORD, ptY3 * MM_TO_COORD, ptZ3 * MM_TO_COORD});

    int64_t idSurface = std::stoull(SurfaceID);
    int64_t idBoundary = std::stoull(OriginalBoundaryID);

    IDispatchPtr pSurface = ApiUtils::FindEntityByID(idSurface);
    IDispatchPtr pBoundary = ApiUtils::FindEntityByID(idBoundary);
    pt::PartDocumentPtr pPartDoc = SEAPP->GetActiveDocument();
    if (nullptr == pPartDoc || nullptr == pSurface || nullptr == pBoundary) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    pt::Sketch3DPtr pSketch3D = ApiUtils::GetFirstSketch3D();
    LPDISPATCH pVShapeDart = nullptr;
    HRESULT hr = pSketch3D->GetBSplineCurves3D()->CreateVShapeDartTG(
        pBoundary.GetInterfacePtr(), pSurface.GetInterfacePtr(),
        ptFst.GetSafeArrayPtr(), ptMid.GetSafeArrayPtr(),
        ptLst.GetSafeArrayPtr(), &pVShapeDart);
    if (FAILED(hr)) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    // 得到edgesId
    NewBoundaryID = ApiUtils::GetEntityIDString(pVShapeDart);
    return vf_Success;
  } catch (_com_error const &e) {
    LogComError(__func__, e);
    return vf_Failure;
  }
}

// clang-format off
vfHRESULT VF2Create3Dsection(
    const char *strLaminateName,  // 铺层名
    const char *strCrossSectionCurve, // 截面曲线
    const char *strComponentType, // 组件类型，表示截面到底截取的是铺层还是层.可选项Ply,Layer
    const char *strBoundaryType,  // 边界类型，表示截面到底截取的是净边界还是扩展边界.可选项Net,Extend
    const char *strScaleType,     // 缩放类型，可选项Scaled或Constant;Scaled时缩放材料厚度的倍数, Constant时缩放固定厚度
    double dPlyOrLayerScale,      // Ply/Layer的系数，依据strScaleType各有不同，有时表示直接的单位，有时是缩放倍数
    double dCoreScale,            // 芯的系数 
    const char *strVisStyle,      // 显示样式  可选为Draped,Schematic,Over Core Hybrid
    const char *strVisProfileType, // 剖面样式 可选为Line、Rectangle
    double dMinDistance,          // 最小距离
    double dCorssSectionOffset,   // 基础截面偏移
    bool bCreateCrossSectionGeometry, // 是否创建截面几何体
    const char *strOutputCurveType,   // 输出曲线类型(要么是样条曲线 要么是可链接曲线)
    bool bGeometryLocked,    // 几何锁定
    const char *strDBPath    // 数据库路径
  ) {
  try {
    // clang-format on
    // 0. 参数检查
    BOOST_LOG_TRIVIAL(info) << __func__;
    std::string componentTypeStr = strComponentType;
    std::transform(componentTypeStr.begin(), componentTypeStr.end(),
                   componentTypeStr.begin(), ::tolower);
    if (componentTypeStr != "ply") {
      BOOST_LOG_TRIVIAL(error) << "暂时只支持Ply类型";
      return vf_Failure;
    }

    std::string boundaryTypeStr = strBoundaryType;
    std::transform(boundaryTypeStr.begin(), boundaryTypeStr.end(),
                   boundaryTypeStr.begin(), ::tolower);
    if (boundaryTypeStr != "net" && boundaryTypeStr != "extend") {
      BOOST_LOG_TRIVIAL(error) << "参数非法,只支持Net和Extend类型";
      return vf_Failure;
    }

    std::string scaleTypeStr = strScaleType;
    std::transform(scaleTypeStr.begin(), scaleTypeStr.end(),
                   scaleTypeStr.begin(), ::tolower);
    if (scaleTypeStr != "scaled" && scaleTypeStr != "constant") {
      BOOST_LOG_TRIVIAL(error) << "参数非法,只支持Scaled和Constant类型";
      return vf_Failure;
    }

    std::string profileTypeStr = strVisProfileType;
    std::transform(profileTypeStr.begin(), profileTypeStr.end(),
                   profileTypeStr.begin(), ::tolower);
    if (profileTypeStr != "line" && profileTypeStr != "rectangle") {
      BOOST_LOG_TRIVIAL(error) << "参数非法,只支持Line和Rectangle类型";
      return vf_Failure;
    }

    std::string visStyleStr = strVisStyle;
    std::transform(visStyleStr.begin(), visStyleStr.end(), visStyleStr.begin(),
                   ::tolower);
    if (visStyleStr != "draped" && visStyleStr != "schematic" &&
        visStyleStr != "over core hybrid") {
      BOOST_LOG_TRIVIAL(error)
          << "参数非法,只支持Draped,Schematic,Over Core Hybrid类型";
      return vf_Failure;
    }

    bool bIsConstant = (scaleTypeStr == "constant");
    bool bIsPly = true;
    bool bIsNet = (boundaryTypeStr == "net");
    bool bIsRectangle = (profileTypeStr != "line");

    if (!bIsPly && !bIsNet) {
      BOOST_LOG_TRIVIAL(error) << "参数非法,Layer类型不支持扩展边界";
      return vf_Failure;
    }

    // 1. 从数据库中取得必要的数据
    LaminateRelateData laminateData;
    if (vf_Success !=
        QueryLaminateRelateData(laminateData, strLaminateName, strDBPath)) {
      return vf_Failure;
    }

    LaminateDesc &laminateDesc = laminateData.Desc;
    std::vector<PlyDesc> &plyDescs = laminateData.PlyList;
    std::map<std::string, MaterialDesc> &materialMap = laminateData.MaterialMap;

    // 2.按Step排序各Ply, Ply间可能重复(只是区域不同，然而所有材料是一致的)
    // 取得各Step的厚度
    // todo 有时Sequence与Step一起决定顺序，后面排序须考虑
    std::sort(plyDescs.begin(), plyDescs.end(),
              [](PlyDesc const &plyDesc1, PlyDesc const &plyDesc2) -> bool {
                return plyDesc1.Step < plyDesc2.Step;
              });

    std::map<int, double> offsetDisArr;
    for (auto &plyDesc : plyDescs) {
      plyDesc.Thickness = materialMap[plyDesc.Material].Thickness;

      // todo 区分Core和普通的Ply和Player,暂时不支持
      bool bIsCore = false;
      // 此层的偏移距离
      if (bIsConstant) {
        // 常量时
        plyDesc.OuterOffsetDistance = bIsCore ? dCoreScale : dPlyOrLayerScale;
      } else {
        plyDesc.OuterOffsetDistance =
            bIsCore ? dCoreScale * plyDesc.Thickness
                    : dPlyOrLayerScale * plyDesc.Thickness;
      }
      offsetDisArr[plyDesc.Step] = plyDesc.OuterOffsetDistance;
    }

    // 3.累加偏移值
    double offsetTotalDis = dCorssSectionOffset;
    for (auto iter = offsetDisArr.begin(); iter != offsetDisArr.end(); iter++) {
      iter->second += offsetTotalDis;
      offsetTotalDis = iter->second;
    }

    for (auto &plyDesc : plyDescs) {
      plyDesc.Thickness = plyDesc.OuterOffsetDistance;
      plyDesc.OuterOffsetDistance = offsetDisArr[plyDesc.Step];
    }

    // 3.绘制
    pt::PartDocumentPtr pPartDoc = SEAPP->GetActiveDocument();
    if (nullptr == pPartDoc) {
      BOOST_LOG_TRIVIAL(error)
          << "未找到活动文档" << "," << __func__ << "," << __LINE__;
      return vf_Failure;
    }

    // 创建草图应放于较前位置，否则后面的拓扑对象有失效的可能
    pt::Sketch3DPtr pSketch = pPartDoc->GetSketches3D()->Add();

    ATL::CComSafeArray<LPDISPATCH> saSectionCurves(plyDescs.size());
    ATL::CComSafeArray<double> saThickness(plyDescs.size());
    ATL::CComSafeArray<double> saOuterOffset(plyDescs.size());
    ATL::CComSafeArray<BSTR> saPlyNames(plyDescs.size());
    ATL::CComSafeArray<long> saColors(plyDescs.size());
    for (long i = 0; i < plyDescs.size(); i++) {
      int64_t curveID =
          bIsNet ? plyDescs[i].NetBoundary : plyDescs[i].ExtBoundary;
      IDispatchPtr pCv = ApiUtils::FindEntityByID(curveID);
      if (nullptr == pCv) {
        BOOST_LOG_TRIVIAL(error) << "边界ID无对应拓扑对象:" << curveID << ","
                                 << __func__ << "," << __LINE__;
        return vf_Failure;
      }
      saSectionCurves[i] = pCv;
      saThickness[i] = plyDescs[i].Thickness;
      saOuterOffset[i] = plyDescs[i].OuterOffsetDistance;
      saPlyNames[i] = plyDescs[i].Name.c_str();

      MaterialDesc &material = materialMap[plyDescs[i].Material];
      std::string specifiedOriention = plyDescs[i].SpecifiedOrientation;
      long clr;
      if ("0" == specifiedOriention) {
        clr = material.Color0;
      } else if ("45" == specifiedOriention) {
        clr = material.Color45;
      } else if ("90" == specifiedOriention) {
        clr = material.Color90;
      } else if ("135" == specifiedOriention) {
        clr = material.Color135;
      } else {
        clr = material.ColorOther;
      }
      saColors[i] = clr;
    }

    int64_t surfaceID = laminateDesc.LayupSurface;
    IDispatchPtr pSurface = ApiUtils::FindEntityByID(surfaceID);
    if (nullptr == pSurface) {
      BOOST_LOG_TRIVIAL(error) << "铺设曲面无对应拓扑对象:" << surfaceID << ","
                               << __func__ << "," << __LINE__;
      return vf_Failure;
    }

    std::string sectionIdStr = strCrossSectionCurve;
    int64_t sectionID = std::stoull(sectionIdStr);
    IDispatchPtr pSectionCv = ApiUtils::FindEntityByID(sectionID);
    if (nullptr == pSectionCv) {
      BOOST_LOG_TRIVIAL(error) << "截面曲线无对应拓扑对象:" << sectionID << ","
                               << __func__ << "," << __LINE__;
      return vf_Failure;
    }

    LPSAFEARRAY pSaCreatedCurves = nullptr;
    LPSAFEARRAY pSaHiliSets = nullptr;
    LPSAFEARRAY pSaTextBoxes = nullptr;
    pPartDoc->Create3DSection(
        pSurface.GetInterfacePtr(), pSectionCv.GetInterfacePtr(),
        saSectionCurves.GetSafeArrayPtr(), saOuterOffset.GetSafeArrayPtr(),
        saThickness.GetSafeArrayPtr(), saPlyNames.GetSafeArrayPtr(),
        saColors.GetSafeArrayPtr(), pSketch.GetInterfacePtr(),
        (long)bIsRectangle, &pSaCreatedCurves, &pSaHiliSets, &pSaTextBoxes);

    ATL::CComSafeArray<LPDISPATCH> saCreateCurves;
    saCreateCurves.Attach(pSaCreatedCurves);
    ATL::CComSafeArray<LPDISPATCH> saHiliSets;
    saHiliSets.Attach(pSaHiliSets);
    ATL::CComSafeArray<LPDISPATCH> saTextBoxes;
    saTextBoxes.Attach(pSaTextBoxes);

    for (auto i = 0; i < saHiliSets.GetCount(); i++) {
      g_tempObjs.push_back(saHiliSets[i]);
    }
    if (!bCreateCrossSectionGeometry) {
      for (auto i = 0; i < saCreateCurves.GetCount(); i++) {
        g_tempObjs.push_back(saCreateCurves[i]);
      }

      for (auto i = 0; i < saTextBoxes.GetCount(); i++) {
        g_tempObjs.push_back(saTextBoxes[i]);
      }

    } else {
    }
    for (LPDISPATCH &pDisp : g_tempObjs) {
      pDisp->AddRef();
    }

    return vf_Success;
  } catch (_com_error const &e) {
    LogComError(__func__, e);
    return vf_Failure;
  }
}

vfHRESULT vfChangeLinesColorAndWidth(const char *Lines[], int *R, int *G,
                                     int *B, double *Linewidth) {

  try {
    BOOST_LOG_TRIVIAL(info) << __func__;
    std::vector<int64_t> lineIds = ApiUtils::CollectIds(Lines);
    if (lineIds.empty()) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }
    int lineCnt = lineIds.size();
    // 按颜色排序,以减少创建的高亮集个数
    std::map<long, std::vector<int>> clrFacesMap;
    std::vector<long> vecColors;
    std::vector<double> vecWidth;
    for (int i = 0; i < lineCnt; i++) {
      long clr = RGB(R[i], G[i], B[i]);
      clrFacesMap[clr].push_back(i);
      vecColors.push_back(clr);
      vecWidth.push_back(Linewidth[i]);
    }

    // 创建高亮集并高亮
    int width = 4;
    for (auto const &[clr, faceIds] : clrFacesMap) {
      fw::HighlightSetPtr pHiliSet = nullptr;
      auto it = hiliSetsClrMap.find(clr);
      if (it == hiliSetsClrMap.end()) {
        pHiliSet = ApiUtils::FindOrCreateFstHighlightSet(clr, width, true);
        hiliSetsClrMap.insert(std::make_pair(clr, pHiliSet));
      } else {
        pHiliSet = it->second;
      }
      ApiUtils::ThickenAndHighlight(pHiliSet, lineIds);
    }

    // 使用复合文档协议记录每个id对应的颜色
    pt::PartDocumentPtr pPartDoc = SEAPP->GetActiveDocument();
    if (nullptr == pPartDoc) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    // 打开或创建一个存储库,专用于保存颜色、宽度信息
    IStoragePtr pRootStg = nullptr;
    pRootStg = pPartDoc->GetAddInsStorage(
        OLESTR("comc_LinesColorAndWidth"),
        STGM_DIRECT | STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE);

    assert(pRootStg != nullptr);
    HRESULT hr = S_OK;
    for (int i = 0; i < lineCnt; i++) {
      int64_t idCurve = lineIds[i];
      long clr = vecColors[i];
      double width = vecWidth[i];

      std::string streamName = std::to_string(idCurve);
      _bstr_t streamNameBstr = streamName.c_str();

      // 打开或创建流对象
      IStreamPtr pStreamData = nullptr;
      hr = pRootStg->CreateStream(streamNameBstr,
                                  STGM_DIRECT | STGM_CREATE | STGM_READWRITE |
                                      STGM_SHARE_EXCLUSIVE,
                                  0, 0, &pStreamData);
      if (FAILED(hr) || !pStreamData) {
        VF_BOOST_LOG_TRIVAL_LINE;
        return vf_Failure;
      }

      // 写入颜色和宽度数据
      pStreamData->Write(&clr, sizeof(long), nullptr);
      pStreamData->Write(&width, sizeof(double), nullptr);
    }

    // 提交更改
    hr = pRootStg->Commit(STGC_DEFAULT | STGC_OVERWRITE);
    if (FAILED(hr)) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    // 保存到持久化存储
    IPersistStoragePtr pPersistStorage = pPartDoc;
    hr = pPersistStorage->Save(pRootStg, FALSE);
    if (FAILED(hr)) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }
    return vf_Success;
  } catch (_com_error const &e) {
    LogComError(__func__, e);
    return vf_Failure;
  }
}

vfHRESULT vfGetLinesColorAndWidth(const char *Lines[], int *&R, int *&G,
                                  int *&B, double *&Linewidth) {
  try {
    BOOST_LOG_TRIVIAL(info) << __func__;
    std::vector<int64_t> lineIds = ApiUtils::CollectIds(Lines);
    if (lineIds.empty()) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }
    int lineCnt = lineIds.size();

    // 使用复合文档协议取每个id对应的颜色以及线宽
    pt::PartDocumentPtr pPartDoc = SEAPP->GetActiveDocument();
    if (nullptr == pPartDoc) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    // 打开存储对象
    IStoragePtr pRootStg = nullptr;
    pRootStg = pPartDoc->GetAddInsStorage(OLESTR("comc_LinesColorAndWidth"),
                                          STGM_DIRECT | STGM_READ |
                                              STGM_SHARE_EXCLUSIVE);
    if (!pRootStg) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    R = new int[lineCnt];
    G = new int[lineCnt];
    B = new int[lineCnt];
    Linewidth = new double[lineCnt];
    HRESULT hr = S_OK;
    for (int i = 0; i < lineCnt; i++) {
      std::string streamName = std::to_string(lineIds[i]);
      _bstr_t streamNameBstr = streamName.c_str();
      IStreamPtr pStreamSize = nullptr;
      hr = pRootStg->OpenStream(streamNameBstr, NULL,
                                STGM_READ | STGM_SHARE_EXCLUSIVE, 0,
                                &pStreamSize);
      if (nullptr == pStreamSize) {
        // 这个id之前没有记录数据
        BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
        return vf_Failure;
      }
      // 依次读取颜色、宽度
      long clr;
      double lineWidth = 0.0;
      hr = pStreamSize->Read(&clr, sizeof(long), NULL);
      hr = pStreamSize->Read(&lineWidth, sizeof(double), NULL);

      // 根据long值取RGB
      R[i] = clr & 0x000000FF;
      G[i] = (clr & 0x0000FF00) >> 8;
      B[i] = (clr & 0x00FF0000) >> 16;
      Linewidth[i] = lineWidth;
    }
    return vf_Success;
  } catch (_com_error const &e) {
    LogComError(__func__, e);
    return vf_Failure;
  }
}

vfHRESULT vfOffsetCurvesByParallel2(const char *SurfaceID, const char *CurveID,
                                    double x, double y, double z,
                                    int DistanceNums, double *GeodesicDistances,
                                    BSTR GeometricSetName,
                                    BSTR *GeodesicCurvesName,
                                    const char **&OffsetCurves) {
  try {

    // 批量平行偏置
    BOOST_LOG_TRIVIAL(info) << LogUtil::GetLogContent(
        __func__, "SurfaceID:", SurfaceID, "x:", x, "y:", y, "z:", z,
        "CurveID:", CurveID, "DistanceNums:", DistanceNums);

    pt::Sketch3DPtr pSketch3d = ApiUtils::GetFirstSketch3D();
    pt::PartDocumentPtr pPartDoc = SEAPP->GetActiveDocument();
    if (nullptr == pPartDoc || SurfaceID == nullptr || SurfaceID[0] == '\0' ||
        CurveID == nullptr || CurveID[0] == '\0') {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    int64_t surfaceId = std::stoull(SurfaceID);
    int64_t curveId = std::stoull(CurveID);

    IDispatchPtr pFaceOrFace = ApiUtils::FindEntityByID(surfaceId);
    IDispatchPtr pEdgeOrEdges = ApiUtils::FindEntityByID(curveId);
    if (nullptr == pFaceOrFace || nullptr == pEdgeOrEdges) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    // 取起点在此面上的法向，及曲线在此点的切向
    auto GetFirstEdgeFirstVertex = [](IDispatchPtr const &pEdgeOrEdges,
                                      gm::VertexPtr &pFstVtx,
                                      gm::EdgePtr &pFstEdge) -> bool {
      geom_entity_type geoType = ApiUtils::GetGeomType(pEdgeOrEdges);
      if (geoType == vf_curve) {
        // 单个边
        pFstEdge = pEdgeOrEdges;
        pFstVtx = pFstEdge->GetStartVertex();
        return true;
      } else if (geoType == vf_curves) {
        gm::EdgesPtr pEdges = pEdgeOrEdges;
        pFstEdge = pEdges->Item(1l);
        pFstVtx = pFstEdge->GetStartVertex();
        return true;
      }
      return false;
    };

    // 取曲线起点在面上的切向 和法向
    gm::VertexPtr pFstVertex = nullptr;
    gm::EdgePtr pFstEdge = nullptr;
    if (!GetFirstEdgeFirstVertex(pEdgeOrEdges, pFstVertex, pFstEdge)) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    int64_t fstCurveId = ApiUtils::GetEntityID(pFstEdge);
    std::string fstCurveIdStr = std::to_string(fstCurveId);
    int64_t fstPointId = ApiUtils::GetEntityID(pFstVertex);
    std::string fstPointIdStr = std::to_string(fstPointId);
    DVec3d normal, tangent;
    VFAdapter::vfHRESULT status = vfGetUnitTangentVectorAtPoint(
        fstCurveIdStr.c_str(), fstPointIdStr.c_str(), false, tangent.x(),
        tangent.y(), tangent.z());
    if (status != vf_Success) {
      return status;
    }

    status =
        vfGetUnitNormalVectorAtPoint(SurfaceID, fstPointIdStr.c_str(), false,
                                     normal.x(), normal.y(), normal.z());
    if (status != vf_Success) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return status;
    }

    DVec3d dirJustify = normal.cross(tangent);
    DVec3d dirOffset{x, y, z};
    // true时，用曲面法向判定;false时，用曲面反向法向判定
    bool isPositive = true;
    if (dirJustify.dot(dirOffset) < 0) {
      isPositive = false;
    }

    // 收集偏移数组
    std::vector<double> offsetDistances(DistanceNums);
    for (int i = 0; i < DistanceNums; i++) {
      offsetDistances[i] = GeodesicDistances[i] * MM_TO_COORD;
    }

    ATL::CComSafeArray<double> headOfsVals(offsetDistances.size());
    memcpy((*headOfsVals.GetSafeArrayPtr())->pvData, offsetDistances.data(),
           offsetDistances.size() * sizeof(double));

    // 调用
    LPSAFEARRAY offsetedCurves = nullptr;
    HRESULT hr = pPartDoc->BatchOffsetOnFaceCurve(
        pFaceOrFace.GetInterfacePtr(), pEdgeOrEdges.GetInterfacePtr(),
        isPositive, headOfsVals.GetSafeArrayPtr(),
        headOfsVals.GetSafeArrayPtr(), &offsetedCurves);
    if (FAILED(hr)) {
      return vf_Failure;
    }

    ATL::CComSafeArray<IDispatch *> offsetedCurvesArray;
    offsetedCurvesArray.Attach(offsetedCurves);
    std::vector<int64_t> offsetedCurveIDs;
    for (int i = 0; i < offsetedCurvesArray.GetCount(); i++) {
      IDispatch *pDispatch = offsetedCurvesArray[i];
      pt::SurfaceOffsetCurvePtr pOffsetCv = pDispatch;
      assert(pOffsetCv);
      gm::EdgesPtr pEdges = pOffsetCv->GetEdges(gm::igQueryAll);
      for (long j = 0; j < pEdges->GetCount(); j++) {
        gm::EdgePtr pEdge = pEdges->Item(j + 1);
        int64_t edgeID = ApiUtils::GetEntityID(pEdge);
        offsetedCurveIDs.push_back(edgeID);
      }
    }
    OffsetCurves = new const char *[offsetedCurveIDs.size() + 1];
    OffsetCurves[offsetedCurveIDs.size()] = nullptr;
    for (size_t i = 0; i < offsetedCurveIDs.size(); i++) {
      std::string offsetedCurveID = std::to_string(offsetedCurveIDs[i]);
      OffsetCurves[i] = _strdup(offsetedCurveID.c_str());
    }
    BOOST_LOG_TRIVIAL(info)
        << LogUtil::GetLogContent(__func__, "OffsetCurves:", OffsetCurves);

    return vf_Success;
  } catch (_com_error const &e) {
    LogComError(__func__, e);
    return vf_Failure;
  }
}

vfHRESULT vfTestCurvesCoincidence(const char *Curve1ID, const char *Curve2ID,
                                  int &result) {
  try {
    BOOST_LOG_TRIVIAL(info) << LogUtil::GetLogContent(
        __func__, "Curve1ID:", Curve1ID, "Curve2ID:", Curve2ID);
    pt::PartDocumentPtr pPartDoc = SEAPP->GetActiveDocument();
    if (nullptr == pPartDoc) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    if (nullptr == Curve1ID || nullptr == Curve2ID || '\0' == Curve1ID[0] ||
        '\0' == Curve2ID[0]) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    std::string strCvId1 = Curve1ID;
    std::string strCvId2 = Curve2ID;
    int64_t cvId1 = std::stoull(strCvId1);
    int64_t cvId2 = std::stoull(strCvId2);
    IDispatchPtr pCurve1 = ApiUtils::FindEntityByID(cvId1);
    IDispatchPtr pCurve2 = ApiUtils::FindEntityByID(cvId2);
    if (nullptr == pCurve1 || nullptr == pCurve2) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    result = pPartDoc->TestCurveCoincidence(pCurve1.GetInterfacePtr(),
                                            pCurve2.GetInterfacePtr());
    return vf_Success;
  } catch (_com_error const &e) {
    LogComError(__func__, e);
    result = -1;
    return vf_Success;
  }
}

vfHRESULT vfCreateJoin(const char *OriginalCurves[], const char *&JoinCurve) {
  try {
    BOOST_LOG_TRIVIAL(info)
        << LogUtil::GetLogContent(__func__, "OriginalCurves:", OriginalCurves);

    std::vector<int64_t> curveIds = ApiUtils::CollectIds(OriginalCurves);
    if (curveIds.empty()) {
      return vf_Failure;
    }

    int num = curveIds.size();
    pt::PartDocumentPtr pPartDoc = SEAPP->GetActiveDocument();
    if (nullptr == pPartDoc) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }
    ATL::CComSafeArray<IDispatch *> edgeArr(num);
    for (int i = 0; i < num; ++i) {
      gm::EdgePtr pEdge = ApiUtils::FindEntityByID(curveIds[i]);
      if (nullptr == pEdge) {
        BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
        return vf_Failure;
      }
      edgeArr[i] = pEdge.GetInterfacePtr();
    }

    pt::DerivedCurvesPtr pDerivedCvs =
        pPartDoc->GetConstructions()->GetDerivedCurves();

    pt::DerivedCurvePtr pCompCv =
        pDerivedCvs->Add(num, edgeArr.GetSafeArrayPtr(),
                         pt::DerivedCurveTypeConstants::igDCComposite);
    if (nullptr == pCompCv) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    gm::EdgesPtr pEdges = pCompCv->GetEdges(gm::igQueryAll);
    assert(pEdges && pEdges->GetCount() > 0);
    // gm::EdgePtr pEdge = pEdges->Item(1l);
    int64_t JoinCurveId = ApiUtils::GetEntityID(pEdges);
    std::string JoinCurveIdStr = std::to_string(JoinCurveId);
    JoinCurve = _strdup(JoinCurveIdStr.c_str());
    return vf_Success;
  } catch (_com_error const &e) {
    LogComError(__func__, e);
    return vf_Failure;
  }
}

vfHRESULT vf2DeleteTemporaryObjects() {
  try {
    BOOST_LOG_TRIVIAL(info) << __func__;
    // 抑制重计算(刷新)
    SEAPP->PutDelayCompute(true);

    pt::PartDocumentPtr pPartDoc = SEAPP->GetActiveDocument();
    if (nullptr == pPartDoc) {
      return vf_Failure;
    }

    for (LPDISPATCH &pDisp : g_tempObjs) {
      fw::HighlightSetPtr pHiliSet = pDisp;
      if (pHiliSet) {
        // 如果是高亮集合，先清空
        pHiliSet->RemoveAll();
      }
      ApiUtils::InvokeDelete(pDisp);
    }

    g_tempObjs.clear();

    // 恢复重计算(刷新)
    SEAPP->PutDelayCompute(false);
    return vf_Success;
  } catch (_com_error const &e) {
    LogComError(__func__, e);
    return vf_Failure;
  }
}

enum class EnumSummaryType : int {
  none = 0,
  count = 1 << 0,
  thickness = 1 << 1,
  Percent = 1 << 2,            // 角度按个数算百分比
  PercentByThickness = 1 << 3, // 角度按厚度算百分比
  name = 1 << 4
};

enum class EnumComponentType {
  none = 0,
  name = 1 << 0,
  seq_step = 1 << 2,
  orientation = 1 << 3,
  material = 1 << 4,
  thickness = 1 << 5
};

// clang-format off
vfHRESULT vf2Create3DNotes(
    const char *str3DNotesName, // 3维标注名
     const char *strLaminate,   // 层合板名
      const char *strRosette,   // Rosette名
    const char *strRefOrigin,   // 参考点
     bool bAnnotationLocked,    // 注释锁定
    const char *strBoundaryType, // 下拉框两个选项，”Net”/”Extended”
    const char *strAnnotationLeader, // 一个注释
    const char *strSummaryInfo,   // 下拉框六个选项字符串 摘要格式
    const char *strComponentInfo, // 下拉框七个选项字符串 组成格式
    const char *strHeaderText,   // 表头文本
    const char *strColumnSeparator, // 列分隔符
    int iLinesPerColumn,   // 每列的行数
    bool bReverseSequence, // 翻转顺序(默认为false时，从小到大排序;从大到小排序时为true)
    const char *strDBPath) {
  // clang-format on
  try {
    BOOST_LOG_TRIVIAL(info) << LogUtil::GetLogContent(
        __func__, "str3DNotesName:", str3DNotesName,
        "strLaminate:", strLaminate, "strRosette:", strRosette,
        "strRefOrigin:", strRefOrigin, "strBoundaryType:", strBoundaryType,
        "strSummaryInfo:", strSummaryInfo,
        "strComponentInfo:", strComponentInfo, "strHeaderText:", strHeaderText,
        "strColumnSeparator:", strColumnSeparator);

    // 1参数处理
    std::string noteName = str3DNotesName;
    std::string strLaminateName = strLaminate;
    int64_t ptRefOriginId = std::stoull(strRefOrigin);
    std::string boundaryType = strBoundaryType;

    std::transform(boundaryType.begin(), boundaryType.end(),
                   boundaryType.begin(), ::tolower);
    if ("net" != boundaryType && "extended" != boundaryType) {
      BOOST_LOG_TRIVIAL(info)
          << "Invalid boundary type,it should be net or extended";
      return vf_Failure;
    }

    std::string summaryTypeStr = strSummaryInfo;
    std::transform(summaryTypeStr.begin(), summaryTypeStr.end(),
                   summaryTypeStr.begin(), ::tolower);
    int summaryType = 0;
    if ("none" == summaryTypeStr) {
      summaryType = int(EnumSummaryType::none);
    } else if ("count" == summaryTypeStr) {
      summaryType = int(EnumSummaryType::count);
    } else if ("thickness" == summaryTypeStr) {
      summaryType = int(EnumSummaryType::thickness);
    } else if ("count/thickness" == summaryTypeStr) {
      summaryType =
          int((int)EnumSummaryType::count | (int)EnumSummaryType::thickness);
    } else if ("count/thickness/percentage" == summaryTypeStr) {
      summaryType =
          int((int)EnumSummaryType::count | (int)EnumSummaryType::thickness |
              (int)EnumSummaryType::Percent);
    } else if ("count/thickness/percentage by thickness" == summaryTypeStr) {
      summaryType =
          int((int)EnumSummaryType::count | (int)EnumSummaryType::thickness |
              (int)EnumSummaryType::PercentByThickness);
    } else {
      BOOST_LOG_TRIVIAL(error) << "Invalid summary type: " << summaryTypeStr;
      return vf_Failure;
    }

    std::string componentTypeStr = strComponentInfo;
    std::transform(componentTypeStr.begin(), componentTypeStr.end(),
                   componentTypeStr.begin(), ::tolower);
    int componentType = 0;
    if ("none" == componentTypeStr) {
      componentType = int(EnumComponentType::none);
    } else if ("name" == componentTypeStr) {
      componentType = int(EnumComponentType::name);
    } else if ("name/seq-step" == componentTypeStr) {
      componentType =
          int((int)EnumComponentType::name | (int)EnumComponentType::seq_step);
    } else if ("name/seq-step/orientation" == componentTypeStr) {
      componentType =
          int((int)EnumComponentType::name | (int)EnumComponentType::seq_step |
              (int)EnumComponentType::orientation);
    } else if ("name/seq-step/orient/material" == componentTypeStr) {
      componentType =
          int((int)EnumComponentType::name | (int)EnumComponentType::seq_step |
              (int)EnumComponentType::orientation |
              (int)EnumComponentType::material);
    } else if ("name/seq-step/orient/material/thickness" == componentTypeStr) {
      componentType = int(
          (int)EnumComponentType::name | (int)EnumComponentType::seq_step |
          (int)EnumComponentType::orientation |
          (int)EnumComponentType::material | (int)EnumComponentType::thickness);
    } else {
      BOOST_LOG_TRIVIAL(error)
          << "Invalid component type: " << componentTypeStr;
      return vf_Failure;
    }

    bool useNetBoundary = ("net" == boundaryType);
    // 2由点计算得层合板的铺层信息
    // 2.1 从数据库中取得必要的数据
    gm::VertexPtr pVtx = ApiUtils::FindEntityByID(ptRefOriginId);
    if (nullptr == pVtx) {
      VF_BOOST_LOG_TRIVAL_LINE;
      return vf_Failure;
    }
    ATL::CComSafeArray<double> ptData(3);
    pVtx->GetPointData(ptData.GetSafeArrayPtr());
    double xInMM = ptData[0] * COORD_TO_MM;
    double yInMM = ptData[1] * COORD_TO_MM;
    double zInMM = ptData[2] * COORD_TO_MM;
    LaminateRelateData laminateData;
    if (vf_Success !=
        QueryLaminateRelateData(laminateData, strLaminateName, strDBPath)) {
      VF_BOOST_LOG_TRIVAL_LINE;
      return vf_Failure;
    }

    LaminateDesc &laminateDesc = laminateData.Desc;
    std::vector<PlyDesc> &plyDescs = laminateData.PlyList;
    std::map<std::string, MaterialDesc> &materialMap = laminateData.MaterialMap;
    // 2.2 查询得覆盖此点的所有铺层
    std::vector<PlyDesc> coverPointPlyList;
    for (int i = 0; i < plyDescs.size(); i++) {
      PlyDesc const &plyDesc = plyDescs[i];
      int64_t boundaryId =
          useNetBoundary ? plyDesc.NetBoundary : plyDesc.ExtBoundary;
      int64_t surfaceId = laminateData.Desc.LayupSurface;
      std::string strBoundary = std::to_string(boundaryId);
      std::string strSurface = std::to_string(surfaceId);

      int res = -1;
      vfHRESULT status = vfTestPointAndBoundary(
          xInMM, yInMM, zInMM, strSurface.c_str(), strBoundary.c_str(), res);
      if (res == 1) {
        // 点在边界内
        coverPointPlyList.push_back(plyDesc);
      }
    }

    int cnt = coverPointPlyList.size();
    double thkTotal = 0;
    for (auto &ply : coverPointPlyList) {
      ply.Thickness = materialMap[ply.Material].Thickness;
      thkTotal += ply.Thickness;
    }
    // 2.3 默认按从小到大排序
    std::sort(coverPointPlyList.begin(), coverPointPlyList.end(),
              [](PlyDesc const &lhs, PlyDesc const &rhs) -> bool {
                // 默认按seq排序
                // seq相同时，按step排序
                if (!lhs.Sequence.empty() && !rhs.Sequence.empty() &&
                    lhs.Sequence[0] != rhs.Sequence[0]) {
                  // 此处假定Sequence一般为大写的单字符
                  return lhs.Sequence[0] < rhs.Sequence[0];
                }
                return lhs.Step < rhs.Step;
              });
    if (bReverseSequence) {
      std::reverse(coverPointPlyList.begin(), coverPointPlyList.end());
    }

    // 3格式化铺层信息
    std::string headerStr = strHeaderText;
    std::string separatorStr = strColumnSeparator;
    separatorStr = " " + separatorStr + " ";
    // 3.1 得摘要
    std::string summary = "";
    if (summaryType != 0) {
      std::string nameStr = strLaminateName;
      bool hasCount = (summaryType & (int)EnumSummaryType::count);
      bool hasThk = (summaryType & (int)EnumSummaryType::thickness);
      bool hasPercentageByCnt = (summaryType & (int)EnumSummaryType::Percent);
      bool hasPercentByThickness =
          (summaryType & (int)EnumSummaryType::PercentByThickness);

      std::string percentStr = "";
      if (hasPercentageByCnt || hasPercentByThickness) {
        std::string percentCntStr;
        std::string percentByThkStr;
        // 按角度进行分类，并统计此角度对应的个数及厚度
        std::map<std::string, std::pair<int, double>> sortRes;
        for (auto const &ply : coverPointPlyList) {
          if (!sortRes.count(ply.SpecifiedOrientation)) {
            sortRes[ply.SpecifiedOrientation] =
                std::make_pair<int, double>(0, 0.0);
          }
          sortRes[ply.SpecifiedOrientation].first++;
          sortRes[ply.SpecifiedOrientation].second += ply.Thickness;
        }
        std::vector<std::string> vecPercentTitle{"Orient"};
        std::vector<std::string> vecCntPercent{"%"};
        std::vector<std::string> vecThkPercent{"%"};
        for (auto const &orientPair : sortRes) {
          vecPercentTitle.push_back(orientPair.first);
          int cntPercent =
              100 * (double)orientPair.second.first / coverPointPlyList.size();
          vecCntPercent.push_back(std::to_string(cntPercent));
          int thkPercent = 100 * orientPair.second.second / thkTotal;
          vecThkPercent.push_back(std::to_string(thkPercent));
        }
        percentCntStr =
            hasPercentageByCnt ? boost::join(vecCntPercent, separatorStr) : "";
        percentByThkStr = hasPercentByThickness
                              ? boost::join(vecThkPercent, separatorStr)
                              : "";
        std::string percentTitle = boost::join(vecPercentTitle, separatorStr);
        if (hasPercentageByCnt) {
          percentStr = percentTitle + "\n" + percentCntStr;
        }
        if (hasPercentByThickness) {
          percentStr = percentTitle + "\n" + percentByThkStr;
        }
      }

      std::string countStr = hasCount ? std::to_string(cnt) : "";
      std::string thkStr = hasThk ? std::format("{:.6f}", thkTotal) : "";
      std::string summaryTitle = strSummaryInfo;
      std::string summaryContent = std::format(
          "{0}{1}{2}", countStr, (hasCount && hasThk) ? "/" : "", thkStr);
      summary = std::format("{0}\n{1}", summaryTitle, summaryContent);
      if (!percentStr.empty()) {
        summary += ("\n" + percentStr);
      }
    }

    // 3.2 得每一行表格
    std::vector<std::string> vecRowStr;

    if (componentType != (int)EnumComponentType::none) {
      bool hasName = (componentType & (int)EnumComponentType::name);
      bool hasSeqStep = (componentType & (int)EnumComponentType::seq_step);
      bool hasOrientation =
          (componentType & (int)EnumComponentType::orientation);
      bool hasMaterial = (componentType & (int)EnumComponentType::material);
      bool hasThk = (componentType & (int)EnumComponentType::thickness);

      for (auto idx : std::views::iota(0, (int)coverPointPlyList.size())) {
        PlyDesc const &plyDesc = coverPointPlyList[idx];
        std::string nameStr = (hasName) ? plyDesc.Name : "";
        std::string seqStepStr =
            (hasSeqStep) ? std::format("{0}{1}", plyDesc.Sequence, plyDesc.Step)
                         : "";
        std::string orientationStr =
            (hasOrientation) ? plyDesc.SpecifiedOrientation : "";
        std::string materialStr = hasMaterial ? plyDesc.Material : "";
        std::string thkStr =
            hasThk ? std::format("{0:.6f}", plyDesc.Thickness) : "";
        std::vector<std::string> vecColumnStr{
            nameStr, seqStepStr, orientationStr, materialStr, thkStr};
        auto it = std::remove(vecColumnStr.begin(), vecColumnStr.end(), "");
        if (it != vecColumnStr.end()) {
          vecColumnStr.erase(it, vecColumnStr.end());
        }
        std::string rowStr = boost::join(vecColumnStr, separatorStr);
        vecRowStr.push_back(rowStr);
      }
    }

    std::string tableTitle = strComponentInfo;
    if (iLinesPerColumn != INT_MAX &&
        coverPointPlyList.size() > iLinesPerColumn) {
      // 限定每列的行数
      for (int i = iLinesPerColumn; i < coverPointPlyList.size(); i++) {
        vecRowStr[i % iLinesPerColumn] += (separatorStr + vecRowStr[i]);
      }
      vecRowStr.resize(iLinesPerColumn);

      int nBlockCnt = (coverPointPlyList.size() / iLinesPerColumn);
      if (0 != coverPointPlyList.size() % iLinesPerColumn) {
        nBlockCnt += 1;
      }
      nBlockCnt -= 1;
      std::string orgTitle = tableTitle;
      for (int i = 0; i < nBlockCnt; i++) {
        tableTitle += (separatorStr + orgTitle);
      }
    }

    std::string tableStr = boost::join(vecRowStr, "\n");

    // 4.更改注释所指文本
    std::vector<std::string> vecNoteContents{headerStr, summary, tableTitle,
                                             tableStr};
    auto it = std::remove(vecNoteContents.begin(), vecNoteContents.end(), "");
    if (it != vecNoteContents.end()) {
      vecNoteContents.erase(it, vecNoteContents.end());
    }
    std::string noteContent = boost::join(vecNoteContents, "\n");
    pt::PartDocumentPtr pPartDoc = SEAPP->GetActiveDocument();
    if (nullptr == pPartDoc) {
      VF_BOOST_LOG_TRIVAL_LINE;
      return vf_Failure;
    }

    std::string strNoteId = strAnnotationLeader;
    int64_t noteTextId = std::stoull(strNoteId);
    fwp::BalloonPtr pNote = ApiUtils::FindFeatureByID(-1 * noteTextId);
    if (nullptr == pNote) {
      VF_BOOST_LOG_TRIVAL_LINE;
      return vf_Failure;
    }

    // ballonText 使用\r换行才显示正确
    boost::replace_all(noteContent, "\n", "\r");

    pNote->PutCalloutTextWidthMode(fwp::igDimCalloutFitToContent);
    pNote->PutBalloonText(noteContent.c_str());
    return vf_Success;
  } catch (_com_error const &e) {
    LogComError(__func__, e);
    return vf_Failure;
  }
}

vfHRESULT vfGetActiveDocumentPath(BSTR &ActiveDocumentPath) {
  try {
    BOOST_LOG_TRIVIAL(info) << __func__;
    fw::SolidEdgeDocumentPtr pPartDoc = SEAPP->GetActiveDocument();
    if (nullptr == pPartDoc) {
      VF_BOOST_LOG_TRIVAL_LINE;
      return vf_Failure;
    }
    _bstr_t strPath = pPartDoc->GetFullName();
    ActiveDocumentPath = strPath.Detach();
    return vf_Success;
  } catch (_com_error const &e) {
    LogComError(__func__, e);
    return vf_Failure;
  }
}

vfHRESULT vfGetActiveDocumentType(BSTR &ActiveDocumentType) {
  try {
    BOOST_LOG_TRIVIAL(info) << __func__;
    fw::DocumentTypeConstants docType = SEAPP->GetActiveDocumentType();
    switch (docType) {
    case fw::DocumentTypeConstants::igPartDocument:
      ActiveDocumentType = SysAllocString(L"CATPart");
      break;
    case fw::DocumentTypeConstants::igAssemblyDocument:
      ActiveDocumentType = SysAllocString(L"CATProduct");
      break;
    case fw::DocumentTypeConstants::igDraftDocument:
      ActiveDocumentType = SysAllocString(L"CATDrawing");
      break;
    default:
      return vf_Failure;
    }
    return vf_Success;
  } catch (_com_error const &e) {
    LogComError(__func__, e);
    return vf_Failure;
  }
}

vfHRESULT vfPickPointCoord(const char *SurfaceID, int PickNums, double *&X,
                           double *&Y, double *&Z) {
  try {
    BOOST_LOG_TRIVIAL(info)
        << LogUtil::GetLogContent(__func__, "surfaceID:", SurfaceID);
    // 发送消息给插件，启动在指定面上点击命令,并等待消息
    int idSurface = std::stoull(SurfaceID);
    IDispatchPtr pFaceOrFaces = ApiUtils::FindEntityByID(idSurface);
    if (nullptr == pFaceOrFaces) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    GetMousePointArg arg(idSurface, PickNums);
    std::string msgReceived;
    if (vf_Success != MsgSendAndReceive(msgReceived, arg)) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    BOOST_LOG_TRIVIAL(info) << "msgReceived: " << msgReceived;
    GetMousePointReturnData retData =
        GetMousePointReturnData::FromJson(msgReceived);
    if (retData.ErrorCode != 0) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    X = new double[PickNums + 1];
    Y = new double[PickNums + 1];
    Z = new double[PickNums + 1];
    X[PickNums] = Y[PickNums] = Z[PickNums] = NAN;

    for (int i = 0; i < PickNums; i++) {
      double x = retData.X[i];
      double y = retData.Y[i];
      double z = retData.Z[i];
      long bOn = -1;
      if (vf_Success !=
          GetMinimumDistancePointCoordinates(pFaceOrFaces, x, y, z, bOn)) {
        BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
        return vf_Failure;
      }

      X[i] = x * COORD_TO_MM;
      Y[i] = y * COORD_TO_MM;
      Z[i] = z * COORD_TO_MM;
    }

    // for (int i = 0; i < PickNums; i++) {
    //   const char *ptID = nullptr;
    //   vfCreatePoint(X[i], Y[i], Z[i], ptID);
    //   if (ptID) {
    //     delete ptID;
    //   }
    // }
    BOOST_LOG_TRIVIAL(info) << LogUtil::GetLogContent(
        __func__, "surfaceID:", SurfaceID, "X:", X, "Y:", Y, "Z:", Z);

    return vf_Success;
  } catch (_com_error const &e) {
    LogComError(__func__, e);
    BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
    return vf_Failure;
  }
}

vfHRESULT vfGetNewBoundary(const char *OriginalCurveID, double x, double y,
                           double z, const char *DivideCurveID,
                           const char *&NewBoundaryID) {
  try {
    BOOST_LOG_TRIVIAL(info) << LogUtil::GetLogContent(
        __func__, "OriginalCurveID:", OriginalCurveID, "x:", x, "y:", y,
        "z:", z, "DivideCurveID:", DivideCurveID);

    int64_t originalCvId = std::stoull(OriginalCurveID);
    int64_t divideCvId = std::stoull(DivideCurveID);
    IDispatchPtr pOriginalCv = ApiUtils::FindEntityByID(originalCvId);
    IDispatchPtr pDivideCv = ApiUtils::FindEntityByID(divideCvId);
    pt::PartDocumentPtr pDoc = SEAPP->GetActiveDocument();
    if (pOriginalCv == nullptr || pDivideCv == nullptr || pDoc == nullptr) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    pt::Sketch3DPtr pFstSketch = ApiUtils::GetFirstSketch3D();
    ATL::CComSafeArray<double> ptData = ApiUtils::ToComSafeArray(
        {x * MM_TO_COORD, y * MM_TO_COORD, z * MM_TO_COORD});

    LPSAFEARRAY pSaCvs = nullptr;
    pDoc->BoundaryTrimCombine(
        pOriginalCv.GetInterfacePtr(), pDivideCv.GetInterfacePtr(),
        ptData.GetSafeArrayPtr(), pFstSketch.GetInterfacePtr(), &pSaCvs);
    if (nullptr == pSaCvs) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    gm::EdgesPtr pEdges = ApiUtils::ExtractTopoEdges(pSaCvs);
    int64_t newBoundaryId = ApiUtils::GetEntityID(pEdges);
    std::string newBoundaryIdStr = std::to_string(newBoundaryId);
    NewBoundaryID = _strdup(newBoundaryIdStr.c_str());
    return vf_Success;
  } catch (_com_error const &e) {
    LogComError(__func__, e);
    return vf_Failure;
  }
}

vfHRESULT vfSurfacesJointAndGetBoundary(const char *Surfaces[],
                                        const char *&NewBoundaryID) {
  try {
    BOOST_LOG_TRIVIAL(info)
        << LogUtil::GetLogContent(__func__, "Surfaces:", Surfaces);

    pt::PartDocumentPtr pDoc = SEAPP->GetActiveDocument();
    if (pDoc == nullptr) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    // get all faces
    std::vector<int64_t> vecSurfaceId = ApiUtils::CollectIds(Surfaces);
    std::vector<LPDISPATCH> vecFaces;
    for (auto id : vecSurfaceId) {
      IDispatchPtr pDisp = ApiUtils::FindEntityByID(id);
      pDisp->AddRef();
      vecFaces.push_back(pDisp);
    }
    int num = (int)vecFaces.size();
    ATL::CComSafeArray<IDispatch *> aSurfaces(num);
    for (int i = 0; i < num; i++) {
      aSurfaces[i] = vecFaces[i];
    }

    gm::EdgesPtr pBoundary =
        pDoc->GetSurfacesBoundary(aSurfaces.GetSafeArrayPtr());
    NewBoundaryID = ApiUtils::GetEntityIDString(pBoundary);
    BOOST_LOG_TRIVIAL(info) << LogUtil::GetLogContent(
        __func__, "Surfaces:", Surfaces, "NewBoundaryID:", NewBoundaryID);
    return vf_Success;
  } catch (_com_error const &e) {
    LogComError(__func__, e);
    return vf_Failure;
  }
}

vfHRESULT vfTestCoincideSurface(const char *Surface1, const char *Surface2,
                                const char *&CoincideLine, int &result) {
  try {
    BOOST_LOG_TRIVIAL(info) << LogUtil::GetLogContent(
        __func__, "Surface1:", Surface1, "Surface2:", Surface2);

    int64_t idSurface1 = std::stoull(Surface1);
    int64_t idSurface2 = std::stoull(Surface2);
    IDispatchPtr pSurfaceFst = ApiUtils::FindEntityByID(idSurface1);
    IDispatchPtr pSurfaceSnd = ApiUtils::FindEntityByID(idSurface2);

    if (pSurfaceFst == nullptr || pSurfaceSnd == nullptr) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    pt::PartDocumentPtr pDoc = SEAPP->GetActiveDocument();
    pt::Sketch3DPtr pSketch = ApiUtils::GetFirstSketch3D();
    LPDISPATCH pCoincidenceCv = nullptr;
    result = pDoc->TestSurfaceCoincidence(pSurfaceFst.GetInterfacePtr(),
                                          pSurfaceSnd.GetInterfacePtr(),
                                          pSketch, &pCoincidenceCv);
    if (1 == result) {
      // 完全重合时，返回的直接就是已存在的拓扑边
      gm::EdgePtr pEdge = pCoincidenceCv;
      if (pEdge == nullptr) {
        // 部分重合，返回的是一个三维草图中新创建的曲线
        pEdge = ApiUtils::EntityToGeomtry(pCoincidenceCv);
      }
      int64_t lineId = ApiUtils::GetEntityID(pEdge);
      std::string lineStr = std::to_string(lineId);
      CoincideLine = _strdup(lineStr.c_str());
    }

    return vf_Success;
  } catch (_com_error const &e) {
    LogComError(__func__, e);
    return vf_Failure;
  }
}

vfHRESULT vfGetPointDirSurfaceIntersections(const char *Surface, double Px,
                                            double Py, double Pz, double Dx,
                                            double Dy, double Dz,
                                            int &PointNumber, double *&X,
                                            double *&Y, double *&Z) {
  try {
    BOOST_LOG_TRIVIAL(info) << LogUtil::GetLogContent(
        __func__, "Surface:", Surface, "Px:", Px, "Py:", Py, "Pz:", Pz,
        "Dx:", Dx, "Dy:", Dy, "Dz:", Dz);
    int64_t idSurface = std::stoull(Surface);
    IDispatchPtr pSurface = ApiUtils::FindEntityByID(idSurface);
    if (pSurface == nullptr) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    pt::PartDocumentPtr pDoc = SEAPP->GetActiveDocument();
    ATL::CComSafeArray<double> ptSpace = ApiUtils::ToComSafeArray(
        {Px * MM_TO_COORD, Py * MM_TO_COORD, Pz * MM_TO_COORD});
    ATL::CComSafeArray<double> rayDir = ApiUtils::ToComSafeArray(
        {Dx * MM_TO_COORD, Dy * MM_TO_COORD, Dz * MM_TO_COORD});
    LPSAFEARRAY pIntersectPoints = nullptr;
    pDoc->RaySurfaceIntersect(pSurface.GetInterfacePtr(),
                              ptSpace.GetSafeArrayPtr(),
                              rayDir.GetSafeArrayPtr(), &pIntersectPoints);
    if (pIntersectPoints == nullptr) {
      PointNumber = 0;
      return vf_Success;
    } else {
      ATL::CComSafeArray<double> saIntersection;
      saIntersection.Attach(pIntersectPoints);
      PointNumber = saIntersection.GetCount() / 3;
      X = new double[PointNumber];
      Y = new double[PointNumber];
      Z = new double[PointNumber];
      for (int i = 0; i < PointNumber; i++) {
        X[i] = saIntersection[i * 3];
        Y[i] = saIntersection[i * 3 + 1];
        Z[i] = saIntersection[i * 3 + 2];
      }
    }
    return vf_Success;
  } catch (_com_error const &e) {
    LogComError(__func__, e);
    return vf_Failure;
  }
}

vfHRESULT vfCreateVShape(const char *SurfaceID, const char *OriginalBoundaryID,
                         double ptX1, double ptY1, double ptZ1, double ptX2,
                         double ptY2, double ptZ2, double ptX3, double ptY3,
                         double ptZ3, const char *&NewVshapeID) {

  try {
    return vfCreateVShapeDart(SurfaceID, OriginalBoundaryID, ptX1, ptY1, ptZ1,
                              ptX2, ptY2, ptZ2, ptX3, ptY3, ptZ3, NewVshapeID);
  } catch (_com_error const &e) {
    LogComError(__func__, e);
    return vf_Failure;
  }
}

vfHRESULT vfCreateNewDartBoundary(const char *SurfaceID,
                                  const char *OriginalBoundaryID,
                                  const char *NewVshapeID, double ptX1,
                                  double ptY1, double ptZ1,
                                  const char *&NewBoundaryID) {
  try {

    BOOST_LOG_TRIVIAL(info) << LogUtil::GetLogContent(
        __func__, "SurfaceID:", SurfaceID,
        "OriginalBoundaryID:", OriginalBoundaryID, "NewVshapeID:", NewVshapeID,
        "ptX1:", ptX1, "ptY1:", ptY1, "ptZ1:", ptZ1);
    pt::PartDocumentPtr pDoc = SEAPP->GetActiveDocument();
    if (nullptr == pDoc) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }
    pt::Sketch3DPtr pSketch = pDoc->GetSketches3D()->Add();
    IDispatchPtr pSurface = ApiUtils::FindEntityByID(SurfaceID);
    IDispatchPtr pVShape = ApiUtils::FindEntityByID(NewVshapeID);
    IDispatchPtr pOriginalBoundary =
        ApiUtils::FindEntityByID(OriginalBoundaryID);
    if (nullptr == pSurface || nullptr == pVShape ||
        nullptr == pOriginalBoundary) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    ATL::CComSafeArray<double> ptSpace = ApiUtils::ToComSafeArray(
        {ptX1 * MM_TO_COORD, ptY1 * MM_TO_COORD, ptZ1 * MM_TO_COORD});
    IDispatchPtr pNewBoundary = nullptr;
    pDoc->CreateNewDartBoundary(
        pSketch.GetInterfacePtr(), pSurface.GetInterfacePtr(),
        pOriginalBoundary.GetInterfacePtr(), pVShape.GetInterfacePtr(),
        ptSpace.GetSafeArrayPtr(), &pNewBoundary);
    if (nullptr != pNewBoundary) {
      NewBoundaryID = ApiUtils::GetEntityIDString(pNewBoundary);
      return vf_Success;
    }

    return vf_Failure;
  } catch (_com_error const &e) {
    LogComError(__func__, e);
    return vf_Failure;
  }
}

vfHRESULT vfCreateCurveOnSurfaceByTwoPoints3(const double *X1, const double *Y1,
                                             const double *Z1, const double *X2,
                                             const double *Y2, const double *Z2,
                                             const char *SurfaceID,
                                             const char **&CurveID) {
  try {
    BOOST_LOG_TRIVIAL(info)
        << LogUtil::GetLogContent(__func__, "SurfaceID:", SurfaceID);
    // 数据转换
    pt::PartDocumentPtr pPartDoc = SEAPP->GetActiveDocument();
    if (nullptr == pPartDoc) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    // 首先添加一个新的三维草图
    pt::Sketch3DPtr pFstSketch3d = pPartDoc->GetSketches3D()->Add();
    if (nullptr == pFstSketch3d) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    int64_t idSurface = std::stoull(SurfaceID);
    IDispatchPtr pFace = ApiUtils::FindEntityByID(idSurface); // 曲面
    if (nullptr == pFace) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    geom_entity_type geoType = ApiUtils::GetGeomType(pFace);
    if (geoType != geom_entity_type::vf_face &&
        geoType != geom_entity_type::vf_faces) {
      // 必须是面或面组
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    bool isFaceGrp = (geoType == geom_entity_type::vf_faces);

    pt::BSplineCurves3DPtr pSpCvs = pFstSketch3d->GetBSplineCurves3D();

    // 提取点集
    std::vector<DPoint3d> aPoints, bPoints;
    aPoints.reserve(500);
    bPoints.reserve(500);
    double stX, stY, stZ, enX, enY, enZ;

    // 收集点集,后续可以不收集直接生成曲线，此处收集为了便于调试。
    while (!isnan(*X1)) {
      stX = *X1++;
      stY = *Y1++;
      stZ = *Z1++;
      enX = *X2++;
      enY = *Y2++;
      enZ = *Z2++;
      if (stX != DBL_MAX && enX != DBL_MAX) {
        aPoints.emplace_back(
            DPoint3d{stX * MM_TO_COORD, stY * MM_TO_COORD, stZ * MM_TO_COORD});
        bPoints.emplace_back(
            DPoint3d{enX * MM_TO_COORD, enY * MM_TO_COORD, enZ * MM_TO_COORD});
      }
    }
    if (aPoints.empty()) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    ATL::CComSafeArray<double> aPointsArr(aPoints.size() * 3);
    memcpy((*aPointsArr.GetSafeArrayPtr())->pvData, aPoints[0].data(),
           aPoints.size() * 3 * sizeof(double));

    ATL::CComSafeArray<double> bPointsArr(bPoints.size() * 3);
    memcpy((*bPointsArr.GetSafeArrayPtr())->pvData, bPoints[0].data(),
           bPoints.size() * 3 * sizeof(double));

    auto pSafeArr = aPointsArr.GetSafeArrayPtr();
    auto elemSize = SafeArrayGetElemsize(*pSafeArr);
    int iii = 0;

    // 生成测地线
    LPSAFEARRAY pLinesArr = nullptr;
    pSpCvs->AddByGeodesic(pFace, aPointsArr.GetSafeArrayPtr(),
                          bPointsArr.GetSafeArrayPtr(), VARIANT_TRUE,
                          &pLinesArr);
    if (CurveID && pLinesArr) {
      ATL::CComSafeArray<IDispatch *> arrCurves;
      arrCurves.Attach(pLinesArr);
      pLinesArr = nullptr;

      CurveID = new const char *[arrCurves.GetCount()];
      for (int i = 0; i < arrCurves.GetCount(); i++) {
        IDispatchPtr pCurve{arrCurves[i]};
        IDispatchPtr pEdge = ApiUtils::EntityToGeomtry(pCurve);
        CurveID[i] = ApiUtils::GetEntityIDString(pEdge);
      }
    }
    if (pLinesArr) {
      // 返回的线暂时不用，释放内存
      DestroySafaArray(pLinesArr);
    }
    return vf_Success;
  } catch (const _com_error &e) {
    LogComError(__func__, e);
    BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
    return vf_Failure;
  }
}

vfHRESULT vfTestCurveOnSurface(const char *SurfaceID, const char *CurveRingID,
                               const char *CurveID, int &result) {

  try {
    BOOST_LOG_TRIVIAL(info) << LogUtil::GetLogContent(
        __func__, "SurfaceID:", SurfaceID, "CurveRingID:", CurveRingID,
        "CurveID:", CurveID);

    IDispatchPtr pSurface = ApiUtils::FindEntityByID(SurfaceID);
    IDispatchPtr pCurve = ApiUtils::FindEntityByID(CurveID);
    IDispatchPtr pCurveRing = ApiUtils::FindEntityByID(CurveRingID);
    if (nullptr == pSurface || nullptr == pCurve || nullptr == pCurveRing) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }
    pt::PartDocumentPtr pDoc = SEAPP->GetActiveDocument();
    long res = pDoc->TestCurveLoopLocation(pSurface.GetInterfacePtr(),
                                           pCurve.GetInterfacePtr(),
                                           pCurveRing.GetInterfacePtr());
    result = res;
    return vf_Success;
  } catch (_com_error const &e) {
    LogComError(__func__, e);
    return vf_Failure;
  }
}

vfHRESULT vfCreateSpliteCurve(const char *SurfaceID, const char *CurveID,
                              const char *BoundaryID,
                              const char *&SpliteCurveID) {
  try {
    BOOST_LOG_TRIVIAL(info) << LogUtil::GetLogContent(
        __func__, "SurfaceID:", SurfaceID, "CurveID:", CurveID,
        "BoundaryID:", BoundaryID);
    // 三维草图的创建必须前置
    pt::PartDocumentPtr pDoc = SEAPP->GetActiveDocument();
    pt::Sketch3DPtr pSketch3d = pDoc->GetSketches3D()->Add();

    IDispatch *pSpliteCurve = nullptr;
    IDispatchPtr pSurface = ApiUtils::FindEntityByID(SurfaceID);
    IDispatchPtr pCurve = ApiUtils::FindEntityByID(CurveID);
    IDispatchPtr pBoundary = ApiUtils::FindEntityByID(BoundaryID);
    if (nullptr == pSurface) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }
    pDoc->CreateSpliteCurve(
        pSketch3d.GetInterfacePtr(), pSurface.GetInterfacePtr(),
        pCurve.GetInterfacePtr(), pBoundary.GetInterfacePtr(), &pSpliteCurve);
    if (nullptr != pSpliteCurve) {
      SpliteCurveID = ApiUtils::GetEntityIDString(pSpliteCurve);
      return vf_Success;
    }

    return vf_Failure;
  } catch (_com_error const &e) {
    LogComError(__func__, e);
    return vf_Failure;
  }
}

vfHRESULT vfCreateCurveByBidirection(const char *SurfaceID, const char *LoopID,
                                     double px, double py, double pz, double dx,
                                     double dy, double dz,
                                     const char *&CurveID) {
  try {
    BOOST_LOG_TRIVIAL(info) << LogUtil::GetLogContent(
        __func__, "LoopID:", LoopID, "SurfaceID:", SurfaceID, "px:", px,
        "py:", py, "pz:", pz, "dx:", dx, "dy:", dy, "dz:", dz);

    int64_t idBoundary = std::stoull(LoopID);
    int64_t idSurface = std::stoull(SurfaceID);
    IDispatchPtr pSurface = ApiUtils::FindEntityByID(idSurface);
    IDispatchPtr pBoundary = ApiUtils::FindEntityByID(idBoundary);

    pt::PartDocumentPtr pDoc = SEAPP->GetActiveDocument();
    if (nullptr == pSurface || nullptr == pBoundary || nullptr == pDoc) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    pt::Sketch3DPtr pSketch = pDoc->GetSketches3D()->Add();

    ATL::CComSafeArray<double> ptSpace = ApiUtils::ToComSafeArray(
        {px * MM_TO_COORD, py * MM_TO_COORD, pz * MM_TO_COORD});
    ATL::CComSafeArray<double> dir = ApiUtils::ToComSafeArray({dx, dy, dz});
    IDispatchPtr pSketch3dCv = pDoc->CreateGeodesicCurveByPoint(
        pSurface.GetInterfacePtr(), pBoundary.GetInterfacePtr(), pSketch,
        ptSpace.GetSafeArrayPtr(), dir.GetSafeArrayPtr());
    if (nullptr == pSketch3dCv) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }
    gm::EdgePtr pEdge = ApiUtils::EntityToGeomtry(pSketch3dCv);
    if (nullptr == pEdge) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    int64_t edgeId = ApiUtils::GetEntityID(pEdge);
    std::string edgeIdStr = std::to_string(edgeId);
    CurveID = _strdup(edgeIdStr.c_str());

    return vf_Success;
  } catch (_com_error const &e) {
    LogComError(__func__, e);
    return vf_Failure;
  }
}
vfHRESULT vfCreateLine(double x1, double y1, double z1, double x2, double y2,
                       double z2, const char *&LineID) {
  try {
    BOOST_LOG_TRIVIAL(info)
        << LogUtil::GetLogContent(__func__, "x1:", x1, "y1:", y1, "z1:", z1,
                                  "x2:", x2, "y2:", y2, "z2:", z2);

    int64_t idLine;
    vfHRESULT status = TGCreateLineStartEnd(x1, y1, z1, x2, y2, z2, idLine);
    if (status == vf_Success) {
      std::string lineIdStr = std::to_string(idLine);
      LineID = _strdup(lineIdStr.c_str());
    }

    return status;
  } catch (_com_error const &e) {
    LogComError(__func__, e);
    return vf_Failure;
  }
}

vfHRESULT vfSurfacesJoint(const char *Surfaces[], const char *&NewSurfaceID) {
  try {
    BOOST_LOG_TRIVIAL(info)
        << LogUtil::GetLogContent(__func__, "Surfaces:", Surfaces);

    pt::PartDocumentPtr pDoc = SEAPP->GetActiveDocument();
    if (pDoc == nullptr) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    // get all faces
    std::vector<int64_t> vecSurfaceId = ApiUtils::CollectIds(Surfaces);
    gm::FacesPtr pFaces = nullptr;
    for (auto id : vecSurfaceId) {
      IDispatchPtr pDisp = ApiUtils::FindEntityByID(id);
      gm::FacePtr pFace = pDisp;
      if (pFaces == nullptr) {
        gm::BodyPtr pBody = pFace->GetBody();
        pFaces = pBody->CreateCollection(
            gm::TopologyCollectionTypeConstants::seFaceCollection);
      } else {
        pFaces->Add(pFace);
      }
    }
    if (nullptr == pFaces) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }
    NewSurfaceID = ApiUtils::GetEntityIDString(pFaces);
    return vf_Success;
  } catch (_com_error const &e) {
    LogComError(__func__, e);
    return vf_Failure;
  }
}

vfHRESULT vfTestTypeOfSurfaceAndGetGeneratrixVector(
    const char *SurfaceID, double x1, double y1, double z1, double x2,
    double y2, double z2, double &X, double &Y, double &Z, int &result) {
  try {
    BOOST_LOG_TRIVIAL(info) << LogUtil::GetLogContent(
        __func__, "SurfaceID:", SurfaceID, "x1:", x1, "y1:", y1, "z1:", z1,
        "x2:", x2, "y2:", y2, "z2:", z2);
    // todo

    pt::PartDocumentPtr pDoc = SEAPP->GetActiveDocument();
    IDispatchPtr pSurface = ApiUtils::FindEntityByID(SurfaceID);
    if (nullptr == pSurface) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    ATL::CComSafeArray<double> ptTarget =
        ApiUtils::ToComSafeArray({x1, y1, z1});
    ATL::CComSafeArray<double> ptDir = ApiUtils::ToComSafeArray({x2, y2, z2});
    ATL::CComSafeArray<double> generatrixVector(3);
    long res = pDoc->TestTypeOfSurfaceAndGetGeneratrixVector(
        pSurface.GetInterfacePtr(), ptTarget.GetSafeArrayPtr(),
        ptDir.GetSafeArrayPtr(), generatrixVector.GetSafeArrayPtr());
    result = res;
    if (result == 1 || result == 2) {
      X = generatrixVector[0];
      Y = generatrixVector[1];
      Z = generatrixVector[2];
    }

    return vf_Success;
  } catch (_com_error const &e) {
    LogComError(__func__, e);
    return vf_Failure;
  }
}

vfHRESULT vfGetSurfaceBoundary() {
  try {
    BOOST_LOG_TRIVIAL(info) << __func__;
    return vf_Success;
  } catch (_com_error const &e) {
    LogComError(__func__, e);
    return vf_Failure;
  }
}

vfHRESULT vfImportDat() {
  try {
    BOOST_LOG_TRIVIAL(info) << __func__;
    return vf_Success;
  } catch (_com_error const &e) {
    LogComError(__func__, e);
    return vf_Failure;
  }
}

// 将TreeNode转换为JSON
nlohmann::json toJson(const TreeNode *treeNode) {
  nlohmann::json j;
  j["nodeName"] = treeNode->nodeName;
  j["key"] = treeNode->key;
  j["value"] = treeNode->value;
  j["valueType"] = static_cast<int>(treeNode->valueType);

  nlohmann::json childArray = nlohmann::json::array();
  for (const auto &child : treeNode->childNodes) {
    childArray.push_back(toJson(child.get()));
  }
  j["childNodes"] = childArray;

  return j;
}

vfHRESULT vfCreateMBD(const TreeNode *rootNode1, const TreeNode *rootNode2) {
  try {
    BOOST_LOG_TRIVIAL(info) << __func__;

    pt::PartDocumentPtr pPartDoc = SEAPP->GetActiveDocument();
    if (nullptr == pPartDoc) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }
    // 1 转为json格式文本
    nlohmann::json jsonFile1 = toJson(rootNode1);
    nlohmann::json jsonFile2 = toJson(rootNode2);
    nlohmann::json rootJson;
    rootJson["nodeName"] = "111";
    rootJson["key"] = "root";
    rootJson["value"] = "";
    rootJson["valueType"] = int(ValueType::STRING_TYPE);
    rootJson["childNodes"] = nlohmann::json::array();
    rootJson["childNodes"].push_back(jsonFile1);
    rootJson["childNodes"].push_back(jsonFile2);
    std::string jsonStr = rootJson.dump(-1, ' ', false);

    // 2 持久存储至文件中
    IStoragePtr pRootStg = nullptr;
    pRootStg = pPartDoc->GetAddInsStorage(
        OLESTR("ComacTree"),
        STGM_DIRECT | STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE);

    assert(pRootStg != nullptr);
    HRESULT hr = S_OK;
    // 2.1 如存在名为Detail的流，则清空，否则新建流
    // 打开或创建流对象
    IStreamPtr pDetailStm = nullptr;
    _bstr_t detailStmName = OLESTR("Detail");
    hr = pRootStg->CreateStream(detailStmName,
                                STGM_DIRECT | STGM_CREATE | STGM_READWRITE |
                                    STGM_SHARE_EXCLUSIVE,
                                0, 0, &pDetailStm);
    if (FAILED(hr) || !pDetailStm) {
      VF_BOOST_LOG_TRIVAL_LINE;
      return vf_Failure;
    }

    // 2.2 写入
    size_t jsonStrSize = jsonStr.size();
    ULONG nWritten;
    hr = pDetailStm->Write(&jsonStrSize, sizeof(size_t), &nWritten);
    if (FAILED(hr) || nWritten != sizeof(size_t)) {
      VF_BOOST_LOG_TRIVAL_LINE;
      return vf_Failure;
    }
    hr = pDetailStm->Write(jsonStr.c_str(), jsonStr.size(), &nWritten);
    if (FAILED(hr) || nWritten != jsonStr.size()) {
      VF_BOOST_LOG_TRIVAL_LINE;
      return vf_Failure;
    }
    // 提交更改
    hr = pRootStg->Commit(STGC_DEFAULT | STGC_OVERWRITE);
    if (FAILED(hr)) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }

    // 保存到持久化存储
    IPersistStoragePtr pPersistStorage = pPartDoc;
    hr = pPersistStorage->Save(pRootStg, FALSE);
    if (FAILED(hr)) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }
    pPartDoc->PutDirty(VARIANT_TRUE);

    // 3 通知插件可以读取json并刷新树
    HWND hwndDest;
    if (vf_Success != FindEdgebarWindow(hwndDest)) {
      BOOST_LOG_TRIVIAL(error) << __func__ << " ,  " << __LINE__;
      return vf_Failure;
    }
    pDetailStm = nullptr;
    pRootStg = nullptr;

    SendMessage(hwndDest, WM_REFRESH_TREE, 0, 0);
    return vf_Success;
  } catch (_com_error const &e) {
    LogComError(__func__, e);
    return vf_Failure;
  }
}

} // namespace VFAdapter